This R script is used to validate the points in the ReSurvey database
using RS indicators (NDVI, NDMI, canopy height).
Load libraries
library(tidyverse)
library(here)
library(gridExtra)
library(readxl)
library(scales)
library(sf)
library(rnaturalearth)
library(dtplyr)
library(lme4)
library(lmerTest)
library(car)
library(ggeffects)
library(party)
library(partykit)
library(strucchange)
library(ggparty)
library(caret)
library(moreparty)
library(randomForest)
Define printall function
printall <- function(tibble) {
print(tibble, width = Inf)
}
Load geom_flat_violin plot
source("https://gist.githubusercontent.com/benmarwick/2a1bb0133ff568cbe28d/raw/fb53bd97121f7f9ce947837ef1a4c65a73bffb3f/geom_flat_violin.R")
Read ReSurvey data with RS indicators
db_resurv_RS<-read_tsv(
here("data", "clean", "db_resurv_RS_20250505.csv"),
col_types = cols(
# Dynamically specify EUNIS columns as character
.default = col_guess(), # Default guessing for other columns
EUNISa = col_character(),
EUNISb = col_character(),
EUNISc = col_character(),
EUNISd = col_character(),
EUNISa_1 = col_character(),
EUNISa_2 = col_character(),
EUNISa_3 = col_character(),
EUNISa_4 = col_character(),
EUNISb_1 = col_character(),
EUNISb_2 = col_character(),
EUNISb_3 = col_character(),
EUNISb_4 = col_character(),
EUNISc_1 = col_character(),
EUNISc_2 = col_character(),
EUNISc_3 = col_character(),
EUNISc_4 = col_character(),
EUNISd_1 = col_character(),
EUNISd_2 = col_character(),
EUNISd_3 = col_character(),
EUNISd_4 = col_character(),
EUNISa_1_descr = col_character(),
EUNISb_1_descr = col_character(),
EUNISc_1_descr = col_character(),
EUNISd_1_descr = col_character(),
EUNIS_assignation = col_character(),
EUNISa_2_descr = col_character(),
EUNISa_3_descr = col_character(),
EUNISa_4_descr = col_character(),
EUNISb_2_descr = col_character(),
EUNISb_3_descr = col_character(),
EUNISb_4_descr = col_character(),
EUNISc_2_descr = col_character(),
EUNISc_3_descr = col_character(),
EUNISc_4_descr = col_character(),
EUNISd_2_descr = col_character(),
EUNISd_3_descr = col_character(),
EUNISd_4_descr = col_character()
)
)
No parsing issues!
Some data managenemt
Several EUNIS level 1 assigned
Number of rows where there is more than one EUNIS 1 assigned, and
they are different among them. See what to do with these later! So far I
take EUNISa_1.
nrow(db_resurv_RS %>%
# Rows with more than one EUNIS 1 assigned
filter(!is.na(EUNISb_1)) %>%
filter(EUNISa_1!=EUNISb_1 | EUNISb_1 != EUNISc_1 | EUNISa_1 != EUNISc_1))
[1] 102
See “confusions”:
db_resurv_RS %>%
# Rows with more than one EUNIS 1 assigned
filter(!is.na(EUNISb_1)) %>%
filter(EUNISa_1!=EUNISb_1 | EUNISb_1 != EUNISc_1 | EUNISa_1 != EUNISc_1) %>%
distinct(EUNISa_1, EUNISb_1, EUNISc_1, EUNISd_1)
Define “confusion” columns:
db_resurv_RS <- db_resurv_RS %>%
mutate(EUNIS1_conf_type = case_when(
EUNISa_1 == "R" & EUNISb_1 == "S" ~ "R/S",
EUNISa_1 == "S" & EUNISb_1 == "T" ~ "S/T",
EUNISa_1 == "R" & EUNISb_1 == "R" & EUNISc_1 == "S" ~ "R/S",
EUNISa_1 == "R" & EUNISb_1 == "R" & EUNISc_1 == "S" & EUNISd_1 == "S" ~ "R/S",
EUNISa_1 == "P" & EUNISb_1 == "Q" ~ "P/Q",
TRUE ~ NA_character_),
EUNIS1_conf = !is.na(EUNIS1_conf_type))
Tibble with selected columns
db_resurv_RS_short <- db_resurv_RS %>%
select(PlotObservationID, Country, RS_CODE, `ReSurvey site`, `ReSurvey plot`,
`Manipulate (y/n)`, `Type of manipulation`, Lon_updated, Lat_updated,
`Location method`, `Location uncertainty (m)`, EUNISa_1,
EUNISa_1_descr, EUNISa_2, EUNISa_2_descr, EUNISa_3, EUNISa_3_descr,
EUNISa_4, EUNISa_4_descr, EUNIS1_conf, EUNIS1_conf_type,
date, year, biogeo, unit, year_RS, Lon_RS, Lat_RS,
starts_with("NDVI"), starts_with("NDMI"), starts_with("NDWI"),
starts_with("EVI"), starts_with("SAVI"), canopy_height,
SOS_DOY, SOS_date, NDVI_at_SOS, Peak_DOY, Peak_date, NDVI_at_Peak,
EOS_DOY, EOS_date, NDVI_at_EOS, Season_Length,
S2_data, Landsat_data, CH_data, S2_phen_data)
TO-DO: Missing data checks
Do when all RS data is ready!
Flag when year is different between RS data and ReSurvey db
db_resurv_RS_short <- db_resurv_RS_short %>%
mutate(year_diff = year != year_RS)
db_resurv_RS_short %>% count(year_diff)
2 with different year. RS indices would need to be calculated again
for those.
Flag when coordinates are different between RS data and ReSurvey
db
db_resurv_RS_short <- db_resurv_RS_short %>%
mutate(Lon_diff = case_when(Lon_updated == Lon_RS ~ "NO",
# Sometimes they are only slighly different
abs(Lon_updated - Lon_RS) < 0.01 ~ "SMALL",
is.na(Lon_updated) | is.na(Lon_RS) ~ NA,
TRUE ~ "LARGE"),
Lat_diff = case_when(Lat_updated == Lat_RS ~ "NO",
# Sometimes they are only slighly different
abs(Lat_updated - Lat_RS) < 0.01 ~ "SMALL",
is.na(Lat_updated) | is.na(Lat_RS) ~ NA,
TRUE ~ "LARGE"))
db_resurv_RS_short %>% count(Lon_diff)
db_resurv_RS_short %>% count(Lat_diff)
Very few with large differences (2 for longitude, 6 for latitude). RS
indices would need to be calculated again for those.
If year_diff is TRUE or Lon_diff is LARGE or Lat_diff is LARGE –>
RS indices need to be recalculated. So far, remove those rows from
db_resurv_RS_short.
db_resurv_RS_short <- db_resurv_RS_short %>%
filter(year_diff == FALSE | is.na(year_diff)) %>%
filter(Lon_diff == "NO" |Lon_diff == "SMALL" | is.na(Lon_diff)) %>%
filter(Lat_diff == "NO" |Lat_diff == "SMALL" | is.na(Lat_diff))
TO-DO: Recalculate RS indices for those above
Handle plots that have more than one obs per year
Add column PLOT to data to identify unique plots:
db_resurv_RS_short_PLOT <- db_resurv_RS_short %>%
# Original names give problems, create new vars
mutate(RS_site = `ReSurvey site`, RS_plot = `ReSurvey plot`) %>%
# Convert to data.table for faster processing
lazy_dt() %>%
# Group by the 3 vars that uniquely identify each pl ot
group_by(RS_CODE, RS_site, RS_plot) %>%
# Create a new variable PLOT for each group
mutate(PLOT = .GRP) %>%
# Convert back to tibble
as_tibble() %>%
# Remove unneeded vars
select(-RS_site, -RS_plot)
There should be only one observation of each plot per year.
Plots where there is at least a year with more than one observation,
and where those observations have a different EUNIS assigned:
plots_to_remove <- db_resurv_RS_short_PLOT %>%
group_by(PLOT, year) %>%
summarize(EUNISa_1_n = n_distinct(EUNISa_1, na.rm = TRUE)) %>%
ungroup() %>%
filter(EUNISa_1_n > 1) %>%
distinct(PLOT)
`summarise()` has grouped output by 'PLOT'. You can override using the `.groups` argument.
Remove plots_to_remove from the database:
db_resurv_RS_short_PLOT <- db_resurv_RS_short_PLOT %>%
anti_join(plots_to_remove, by = "PLOT")
Plots and years where there is more than one observation:
plots_to_merge <- db_resurv_RS_short_PLOT %>%
group_by(PLOT, year) %>%
# Plots that have more than one observation per year
filter(n() > 1) %>%
ungroup() %>%
distinct(PLOT)
Summarize plots_to_merge:
plots_to_merge_summ <- db_resurv_RS_short_PLOT %>%
group_by(PLOT, year) %>%
# Plots that have more than one observation per year
filter(n() > 1) %>%
mutate(obs_num = row_number()) %>%
pivot_wider(
names_from = obs_num,
values_from = c(date, PlotObservationID),
names_prefix = "obs_"
) %>%
arrange(PLOT) %>%
summarize(
across(c(Country, RS_CODE, `ReSurvey site`, `ReSurvey plot`,
`Manipulate (y/n)`, `Type of manipulation`, Lon_updated,
Lat_updated, `Location method`, `Location uncertainty (m)`,
EUNISa_1, EUNISa_1_descr, EUNISa_2, EUNISa_2_descr, EUNISa_3,
EUNISa_3_descr, EUNISa_4, EUNISa_4_descr, EUNIS1_conf,
EUNIS1_conf_type, biogeo, unit, year_RS, Lon_RS, Lat_RS, NDVI_max,
NDVI_median, NDVI_min, NDVI_mode, NDVI_p10, NDVI_p90, NDMI_max,
NDMI_median, NDMI_min, NDMI_mode, NDMI_p10, NDMI_p90, NDWI_max,
NDWI_median, NDWI_min, NDWI_mode, NDWI_p10, NDWI_p90, EVI_max,
EVI_median, EVI_min, EVI_mode, EVI_p10, EVI_p90, SAVI_max,
SAVI_median, SAVI_min, SAVI_mode, SAVI_p10, SAVI_p90,
canopy_height, SOS_DOY, SOS_date, Peak_DOY, Peak_date, EOS_DOY,
EOS_date, S2_data, Landsat_data, CH_data, S2_phen_data, year_diff,
Lon_diff, Lat_diff), first),
across(starts_with("date_obs_"), min),
across(starts_with("PlotObservationID_obs_"), min)
) %>%
ungroup()
`summarise()` has grouped output by 'PLOT'. You can override using the `.groups` argument.
Remove plots_to_merge from the database:
db_resurv_RS_short_PLOT <- db_resurv_RS_short_PLOT %>%
anti_join(plots_to_merge, by = "PLOT")
And add plots_to_merge_summ, where each plot and year only has one
row:
db_resurv_RS_short_PLOT <- bind_rows(db_resurv_RS_short_PLOT,
plots_to_merge_summ)
Check that there is only one row per plot and per year:
db_resurv_RS_short_PLOT %>%
group_by(PLOT, year) %>%
# Plots that have more than one observation per year
filter(n() > 1)
So, to sum up what I have done:
- Plots where there is at least a year with more than one observation,
and where those observations have a different EUNIS assigned: Plots
REMOVED from the data
- Plots where there is more than one observation, but observations
have the same EUNIS assigned: kept in the data. Merged so that there is
only one row per year. Info about the different dates (when different)
is kept in columns date_obs_1 - date_obs_40, and info about the
different PlotObservationID is kept in the columns
PlotObservationID_obs_1 - PlotObservationID_obs_40.
Save to clean data
Save clean file for analyses (to be updated continuously due to
updates in ReSurvey database and updates on RS data).
write_tsv(db_resurv_RS_short_PLOT,
here("data", "clean","db_resurv_RS_short_PLOT_20250505.csv"))
Distributions all bioregions
# Define a function to create histograms
plot_histogram <- function(data, x_var, x_label) {
ggplot(data %>%
filter(EUNISa_1 %in% c("T", "R", "S", "Q")),
aes(x = !!sym(x_var))) +
geom_histogram(color = "black", fill = "white") +
labs(x = x_label, y = "Frequency") +
theme_bw()
}
# Define a function to create plots with violin + boxplot + points
distr_plot <- function(data, y_vars, y_labels) {
for (i in seq_along(y_vars)) {
y_var <- y_vars[[i]]
y_label <- y_labels[[i]]
p <- ggplot(data = data %>%
filter(EUNISa_1 %in% c("T", "R", "S", "Q")),
aes(x = EUNISa_1_descr, y = !!sym(y_var), fill = EUNISa_1_descr)) +
geom_flat_violin(position = position_nudge(x = 0.2, y = 0), alpha = 0.8) +
geom_point(aes(y = !!sym(y_var), color = EUNISa_1_descr),
position = position_jitter(width = 0.15), size = 1, alpha = 0.25) +
geom_boxplot(width = 0.2, outlier.shape = NA, alpha = 0.5) +
stat_summary(fun.y = mean, geom = "point", shape = 20, size = 1) +
stat_summary(fun.data = function(x) data.frame(y = max(x) + 0.1,
label = length(x)),
geom = "text", aes(label = ..label..), vjust = 0.5) +
labs(y = y_label, x = "EUNIS level 1") +
scale_x_discrete(labels = function(x) str_wrap(x, width = 15)) +
guides(fill = FALSE, color = FALSE) +
theme_bw() + coord_flip()
print(p)
}
}
HERE: Metrics calculated only for July, not for all year!
NDVI, NDMI, NDWI, SAVI and EVI
Histograms to check that values are ok:
hist_NDVI <- plot_histogram(db_resurv_RS_short_PLOT, "NDVI_max", "NDVI max")
hist_NDMI <- plot_histogram(db_resurv_RS_short_PLOT, "NDMI_max", "NDMI max")
hist_NDWI <- plot_histogram(db_resurv_RS_short_PLOT, "NDWI_max", "NDWI max")
hist_SAVI <- plot_histogram(db_resurv_RS_short_PLOT, "SAVI_max", "SAVI max")
hist_EVI <- plot_histogram(db_resurv_RS_short_PLOT, "EVI_max", "EVI max")
hist_NDVI

hist_NDMI

hist_NDWI

hist_SAVI

hist_EVI # Some values wrong!

hist_EVI_1 <- plot_histogram(db_resurv_RS_short_PLOT %>% filter(EVI_max <= 1),
"EVI_max", "EVI max")
hist_EVI_1

nrow(db_resurv_RS_short_PLOT %>%
filter(EUNISa_1 %in% c("T", "R", "S", "Q")) %>%
filter(EVI_max > 1))
[1] 1130
db_resurv_RS_short_PLOT %>%
filter(EUNISa_1 %in% c("T", "R", "S", "Q"))%>%
filter(EVI_max > 1) %>%
count(biogeo, unit)
So far, remove observations with EVI_max > 1 from stuff using EVI
values.
Distribution plots:
plot_distr_NDVI <- distr_plot(db_resurv_RS_short_PLOT, "NDVI_max", "NDVI max")
plot_distr_NDMI <- distr_plot(db_resurv_RS_short_PLOT, "NDMI_max", "NDMI max")
plot_distr_NDWI <- distr_plot(db_resurv_RS_short_PLOT, "NDWI_max", "NDWI max")
plot_distr_SAVI <- distr_plot(db_resurv_RS_short_PLOT, "SAVI_max", "SAVI max")
plot_distr_EVI <- distr_plot(db_resurv_RS_short_PLOT %>% filter(EVI_max <= 1),
"EVI_max", "EVI max")
plot_distr_NDVI

plot_distr_NDMI

plot_distr_NDWI

plot_distr_SAVI

plot_distr_EVI

CH
plot_distr_CH <- distr_plot(db_resurv_RS_short_PLOT, "canopy_height",
"Canopy height (m)")
plot_distr_CH

Show habitats with CH categories
ggplot(db_resurv_RS_short_PLOT %>%
# Keep only forests, grasslands, shrublands and wetlands
filter(EUNISa_1 %in% c("T", "R", "S", "Q")) %>%
mutate(CH_cat =
factor(
case_when(canopy_height == 0 ~ "0 m",
canopy_height > 0 & canopy_height <= 1 ~ "0-1 m",
canopy_height > 1 & canopy_height <=2 ~ "1-2 m",
canopy_height > 2 & canopy_height <=5 ~ "2-5 m",
canopy_height > 5 & canopy_height <=8 ~ "5-8 m",
canopy_height > 8 ~ "> 8 m",
is.na(canopy_height) ~ NA_character_),
levels = c(
"0 m", "0-1 m", "1-2 m", "2-5 m", "5-8 m", "> 8 m"))),
aes(x = EUNISa_1_descr, fill = CH_cat)) +
geom_bar() + theme_bw() + coord_flip() +
scale_y_continuous(labels = label_number()) +
scale_fill_viridis_d(direction = -1) +
labs(x = "EUNIS level 1", fill = "Canopy height") +
scale_x_discrete(labels = function(x) str_wrap(x, width = 15)) +
theme(legend.position = c(0.8, 0.75),
legend.direction = "vertical")

Stats per habitat type
db_resurv_RS_short_PLOT %>%
# Keep only forests, grasslands, shrublands and wetlands
filter(EUNISa_1 %in% c("T", "R", "S", "Q")) %>%
group_by(EUNISa_1_descr) %>%
summarise(across(canopy_height, list(
mean = mean,
median = median,
sd = sd,
min = min,
max = max
), na.rm = TRUE))
Phenology
Calculate metrics
db_resurv_RS_short_PLOT <- db_resurv_RS_short_PLOT %>%
mutate(
# Difference NDVI between Peak and SOS
diff_Peak_SOS = NDVI_at_Peak - NDVI_at_SOS,
# Difference NDVI between Peak and EOS
diff_Peak_EOS = NDVI_at_Peak - NDVI_at_EOS)
Histograms phenology measures
ggplot(data = db_resurv_RS_short_PLOT %>%
# Keep only forests, grasslands, shrublands and wetlands
filter(EUNISa_1 %in% c("T", "R", "S", "Q") & S2_phen_data == T) %>%
pivot_longer(cols = c(SOS_DOY, Peak_DOY, EOS_DOY), names_to = "name",
values_to = "value"),
aes(x = value)) +
geom_histogram(fill = "white", color = "black") +
facet_grid(biogeo ~ name, scales = "free_y") +
theme_bw()

ggplot(data = db_resurv_RS_short_PLOT %>%
# Keep only forests, grasslands, shrublands and wetlands
filter(EUNISa_1 %in% c("T", "R", "S", "Q") & S2_phen_data == T) %>%
pivot_longer(cols = c(NDVI_at_SOS, NDVI_at_Peak, NDVI_at_EOS),
names_to = "name", values_to = "value"),
aes(x = value)) +
geom_histogram(fill = "white", color = "black") +
facet_grid(biogeo ~ name, scales = "free_y") +
theme_bw()

ggplot(data = db_resurv_RS_short_PLOT %>%
# Keep only forests, grasslands, shrublands and wetlands
filter(EUNISa_1 %in% c("T", "R", "S", "Q") & S2_phen_data == T) %>%
pivot_longer(cols = c(diff_Peak_SOS, diff_Peak_EOS),
names_to = "name", values_to = "value"),
aes(x = value)) +
geom_histogram(fill = "white", color = "black") +
facet_grid(biogeo ~ name, scales = "free_y") +
theme_bw()

ggplot(data = db_resurv_RS_short_PLOT %>%
# Keep only forests, grasslands, shrublands and wetlands
filter(EUNISa_1 %in% c("T", "R", "S", "Q") & S2_phen_data == T) %>%
pivot_longer(cols = c(Season_Length),
names_to = "name", values_to = "value"),
aes(x = value)) +
geom_histogram(fill = "white", color = "black") +
facet_grid(biogeo ~ name, scales = "free_y") +
theme_bw()

Distributions
plot_distr_SOS_DOY <- distr_plot(db_resurv_RS_short_PLOT, "SOS_DOY",
"SOS DOY")
plot_distr_Peak_DOY <- distr_plot(db_resurv_RS_short_PLOT, "Peak_DOY",
"Peak DOY")
plot_distr_EOS_DOY <- distr_plot(db_resurv_RS_short_PLOT, "EOS_DOY",
"EOS DOY")
plot_distr_NDVI_at_SOS <- distr_plot(db_resurv_RS_short_PLOT, "NDVI_at_SOS",
"NDVI at SOS")
plot_distr_NDVI_at_Peak <- distr_plot(db_resurv_RS_short_PLOT, "NDVI_at_Peak",
"NDVI at Peak")
plot_distr_NDVI_at_EOS <- distr_plot(db_resurv_RS_short_PLOT, "NDVI_at_EOS",
"NDVI at EOS")
plot_distr_diff_Peak_SOS <- distr_plot(db_resurv_RS_short_PLOT, "diff_Peak_SOS",
"Difference Peak-SOS")
plot_distr_diff_Peak_EOS <- distr_plot(db_resurv_RS_short_PLOT, "diff_Peak_EOS",
"Difference Peak-EOS")
plot_distr_Season_Length <- distr_plot(db_resurv_RS_short_PLOT, "Season_Length",
"Season Length")
plot_distr_SOS_DOY

plot_distr_Peak_DOY

plot_distr_EOS_DOY

plot_distr_NDVI_at_SOS

plot_distr_NDVI_at_Peak

plot_distr_NDVI_at_EOS

plot_distr_diff_Peak_SOS

plot_distr_diff_Peak_EOS

plot_distr_Season_Length

Distributions per bioregion
# Define a function to create plots with violin + boxplot + points
distr_plot_biogeo <- function(data, y_var, y_label) {
ggplot(data = data %>%
filter(EUNISa_1 %in% c("T", "R", "S", "Q")),
aes(x = EUNISa_1_descr, y = !!sym(y_var), fill = EUNISa_1_descr)) +
geom_flat_violin(position = position_nudge(x = 0.2, y = 0), alpha = 0.8) +
geom_point(aes(y = !!sym(y_var), color = EUNISa_1_descr),
position = position_jitter(width = 0.15), size = 1, alpha = 0.25) +
geom_boxplot(width = 0.2, outlier.shape = NA, alpha = 0.5) +
stat_summary(fun.y = mean, geom = "point", shape = 20, size = 1) +
stat_summary(fun.data = function(x) data.frame(y = max(x) + 0.1,
label = length(x)),
geom = "text", aes(label = ..label..), vjust = 0.5) +
labs(y = y_label, x = "EUNISa_1_descr") +
scale_x_discrete(labels = function(x) str_wrap(x, width = 15)) +
guides(fill = FALSE, color = FALSE) +
theme_bw() + coord_flip() + facet_wrap(~ biogeo)
}
Max. NDVI, NDMI, NDWI, SAVI and EVI
Distribution plots:
plot_distr_NDVI_biogeo <- distr_plot_biogeo(db_resurv_RS_short_PLOT %>%
filter(!is.na(biogeo)),
"NDVI_max", "NDVI max")
plot_distr_NDMI_biogeo <- distr_plot_biogeo(db_resurv_RS_short_PLOT %>%
filter(!is.na(biogeo)),
"NDMI_max", "NDMI max")
plot_distr_NDWI_biogeo <- distr_plot_biogeo(db_resurv_RS_short_PLOT %>%
filter(!is.na(biogeo)),
"NDWI_max", "NDWI max")
plot_distr_SAVI_biogeo <- distr_plot_biogeo(db_resurv_RS_short_PLOT %>%
filter(!is.na(biogeo)),
"SAVI_max", "SAVI max")
plot_distr_EVI_biogeo <- distr_plot_biogeo(db_resurv_RS_short_PLOT %>%
filter(!is.na(biogeo)) %>%
filter(EVI_max <= 1),
"EVI_max", "EVI max")
plot_distr_NDVI_biogeo

plot_distr_NDMI_biogeo

plot_distr_NDWI_biogeo

plot_distr_SAVI_biogeo

plot_distr_EVI_biogeo

BOR missing because there is no EUNIS info for those that have RS
info.
CH
plot_distr_CH_biogeo <- distr_plot_biogeo(db_resurv_RS_short_PLOT,
"canopy_height", "Canopy height (m)")
plot_distr_CH_biogeo

In this plot, those with biogeo = NA are those that do not have S2 or
Landsat data (and thus biogeo has not been assigned), but have CH data.
We should later assign a biogeo based on location.
Phenology
plot_distr_SOS_DOY_biogeo <- distr_plot_biogeo(db_resurv_RS_short_PLOT %>%
filter(!is.na(biogeo)),
"SOS_DOY", "SOS DOY")
plot_distr_Peak_DOY_biogeo <- distr_plot_biogeo(db_resurv_RS_short_PLOT %>%
filter(!is.na(biogeo)),
"Peak_DOY", "Peak DOY")
plot_distr_EOS_DOY_biogeo <- distr_plot_biogeo(db_resurv_RS_short_PLOT %>%
filter(!is.na(biogeo)),
"EOS_DOY", "EOS DOY")
plot_distr_NDVI_at_SOS_biogeo <- distr_plot_biogeo(db_resurv_RS_short_PLOT %>%
filter(!is.na(biogeo)),
"NDVI_at_SOS", "NDVI at SOS")
plot_distr_NDVI_at_Peak_biogeo <- distr_plot_biogeo(db_resurv_RS_short_PLOT %>%
filter(!is.na(biogeo)),
"NDVI_at_Peak", "NDVI at Peak")
plot_distr_NDVI_at_EOS_biogeo <- distr_plot_biogeo(db_resurv_RS_short_PLOT %>%
filter(!is.na(biogeo)),
"NDVI_at_EOS", "NDVI at EOS")
plot_distr_diff_Peak_SOS_biogeo <- distr_plot_biogeo(db_resurv_RS_short_PLOT %>%
filter(!is.na(biogeo)),
"diff_Peak_SOS", "Difference Peak-SOS")
plot_distr_diff_Peak_EOS_biogeo <- distr_plot_biogeo(db_resurv_RS_short_PLOT %>%
filter(!is.na(biogeo)),
"diff_Peak_EOS", "Difference Peak-EOS")
plot_distr_Season_Length_biogeo <- distr_plot_biogeo(db_resurv_RS_short_PLOT %>%
filter(!is.na(biogeo)),
"Season_Length", "Season Length")
plot_distr_SOS_DOY_biogeo

plot_distr_Peak_DOY_biogeo

plot_distr_EOS_DOY_biogeo

plot_distr_NDVI_at_SOS_biogeo

plot_distr_NDVI_at_Peak_biogeo

plot_distr_NDVI_at_EOS_biogeo

plot_distr_diff_Peak_SOS_biogeo

plot_distr_diff_Peak_EOS_biogeo

plot_distr_Season_Length_biogeo

BOR missing because there is no EUNIS info for those that have RS
info.
HERE: Verify SOS-Peak_EOS ODY
ERRORS! Double-check and send to Bea:
db_resurv_RS_short_PLOT %>% filter(SOS_DOY > Peak_DOY)
db_resurv_RS_short_PLOT %>% filter(Peak_DOY > EOS_DOY)
db_resurv_RS_short_PLOT %>% filter(SOS_DOY > EOS_DOY)
db_resurv_RS_short_PLOT %>% filter(NDVI_at_Peak < NDVI_at_SOS)
db_resurv_RS_short_PLOT %>% filter(NDVI_at_Peak < NDVI_at_EOS)
Comparison differential GPS vs. other points
# Define function
distr_plot_GPS <- function(data, y_var, y_label) {
ggplot(data = data %>%
filter(EUNISa_1 %in% c("T", "R", "S", "Q")) %>%
mutate(GPS =
ifelse(
is.na(`Location method`) |
`Location method` != "Location with differential GPS",
"Other",
"Differential GPS")),
aes(x = EUNISa_1_descr, y = !!sym(y_var), fill = EUNISa_1_descr)) +
geom_flat_violin(position = position_nudge(x = 0.2, y = 0), alpha = 0.8) +
geom_point(aes(y = !!sym(y_var), color = EUNISa_1_descr),
position = position_jitter(width = 0.15), size = 1, alpha = 0.25) +
geom_boxplot(width = 0.2, outlier.shape = NA, alpha = 0.5) +
stat_summary(fun.y = mean, geom = "point", shape = 20, size = 1) +
stat_summary(fun.data = function(x) data.frame(y = max(x) + 0.1,
label = length(x)),
geom = "text", aes(label = ..label..), vjust = 0.5) +
labs(y = y_label, x = "EUNIS level 1") +
scale_x_discrete(labels = function(x) str_wrap(x, width = 15)) +
guides(fill = FALSE, color = FALSE) +
theme_bw() + coord_flip() + facet_wrap(~ GPS)
}
plot_distr_NDVI_GPS <- distr_plot_GPS(db_resurv_RS_short_PLOT,
"NDVI_max", "NDVI max")
plot_distr_NDMI_GPS <- distr_plot_GPS(db_resurv_RS_short_PLOT,
"NDMI_max", "NDMI max")
plot_distr_NDWI_GPS <- distr_plot_GPS(db_resurv_RS_short_PLOT,
"NDWI_max", "NDWI max")
plot_distr_SAVI_GPS <- distr_plot_GPS(db_resurv_RS_short_PLOT,
"SAVI_max", "SAVI max")
plot_distr_EVI_GPS <- distr_plot_GPS(db_resurv_RS_short_PLOT %>%
filter(EVI_max <= 1),
"EVI_max", "EVI max")
plot_distr_NDVI_GPS

plot_distr_NDMI_GPS

plot_distr_NDWI_GPS

plot_distr_SAVI_GPS

plot_distr_EVI_GPS

Including PLOT (USE LATER?)
Summarize variables by plot:
db_resurv_RS_short_PLOT_summ <- db_resurv_RS_short_PLOT %>%
filter(EUNISa_1 %in% c("T", "R", "S", "Q")) %>%
filter(S2_data == T | Landsat_data == T) %>%
group_by(PLOT) %>%
summarize(EUNIS1 = if_else(n_distinct(EUNISa_1) > 1, "Change",
unique(EUNISa_1)[1]),
count = n(),
across(starts_with("NDVI"), list(mean = mean, sd = sd),
.names = "{col}_{fn}"))
Maybe use later because now many plots have only one observation,
probably because some Landsat data is missing?
First validation
For T, R, S, Q habitats.
Define a set of rules for a first validation of ALL ReSurvey data. We
can call these “Expert-based” rules.
Number of observations in ReSurvey from the habitats of interest:
nrow(db_resurv_RS_short_PLOT %>%
filter(EUNISa_1 %in% c("T", "R", "S", "Q")))
[1] 181061
Number of observations in ReSurvey from the habitats of interest and
with all RS data:
nrow(db_resurv_RS_short_PLOT %>%
filter(EUNISa_1 %in% c("T", "R", "S", "Q")) %>%
filter(CH_data == T) %>%
filter(S2_data == T | Landsat_data ==T) %>%
filter(S2_phen_data == T))
[1] 12219
db_resurv_RS_short_PLOT_terrestrial <- db_resurv_RS_short_PLOT %>%
filter(EUNISa_1 %in% c("T", "R", "S", "Q"))
Define rules
Create column for first validation based on different indicators,
where “wrong” is noted when the validation rule is not met. Include
EUNIS1 confusions.
db_resurv_RS_short_PLOT_terrestrial %>% count(EUNISa_1, EUNIS1_conf_type)
Define rules:
db_resurv_RS_short_PLOT_terrestrial <-
db_resurv_RS_short_PLOT_terrestrial %>%
mutate(
valid_1_NDWI = case_when(
# Points that are basically water
NDWI_max > 0.3 ~ "wrong",
TRUE ~ NA_character_),
valid_1_CH = case_when(
# T points with low CH
EUNISa_1 == "T" & canopy_height < 8 ~ "wrong",
# S points with low CH
EUNISa_1 =="S" & canopy_height < 5 ~ "wrong",
# R & Q points with high CH
EUNISa_1 %in% c("R", "Q") & canopy_height > 2 ~ "wrong",
TRUE ~ NA_character_),
valid_1_NDVI = case_when(
# T points with low NDVI_max
EUNISa_1 == "T" & NDVI_max < 0.6 ~ "wrong",
# S-R-Q points with low NDVI_max
EUNISa_1 %in% c("R", "S", "Q") & NDVI_max < 0.2 ~ "wrong",
TRUE ~ NA_character_),
# Count how many validation rules are not met
valid_1_count = rowSums(across(c(valid_1_NDWI, valid_1_CH, valid_1_NDVI),
~ . == "wrong"), na.rm = TRUE),
# Points where at least 1 rule not met
valid_1 = if_else(valid_1_count > 0, "At least 1 rule broken",
"No rules broken so far")
)
Plots first validation
ggplot(db_resurv_RS_short_PLOT_terrestrial%>%
mutate(rules_broken = case_when(
valid_1_count == 1 & valid_1_NDWI == "wrong" ~ "NDWI",
valid_1_count == 1 & valid_1_NDVI == "wrong" ~ "NDVI",
valid_1_count == 1 & valid_1_CH == "wrong" ~ "CH",
valid_1_count == 2 &
valid_1_NDWI == "wrong" & valid_1_NDVI == "wrong"~ "NDWI + NDVI",
valid_1_count == 2 &
valid_1_NDWI == "wrong" & valid_1_CH == "wrong"~ "NDWI + CH",
valid_1_count == 2 &
valid_1_NDVI == "wrong" & valid_1_CH == "wrong"~ "NDVI + CH",
valid_1_count == 3 ~ "NDWI + NDVI + CH",
TRUE ~ NA_character_
)),
aes(x = valid_1_count, fill = rules_broken)) +
geom_bar() + labs(x = "Number of broken rules")

db_resurv_RS_short_PLOT_terrestrial %>%
mutate(rules_broken = case_when(
valid_1_count == 1 & valid_1_NDWI == "wrong" ~ "NDWI",
valid_1_count == 1 & valid_1_NDVI == "wrong" ~ "NDVI",
valid_1_count == 1 & valid_1_CH == "wrong" ~ "CH",
valid_1_count == 2 &
valid_1_NDWI == "wrong" & valid_1_NDVI == "wrong"~ "NDWI + NDVI",
valid_1_count == 2 &
valid_1_NDWI == "wrong" & valid_1_CH == "wrong"~ "NDWI + CH",
valid_1_count == 2 &
valid_1_NDVI == "wrong" & valid_1_CH == "wrong"~ "NDVI + CH",
valid_1_count == 3 ~ "NDWI + NDVI + CH",
TRUE ~ NA_character_
)) %>%
count(rules_broken, EUNIS1_conf_type)
Proportion of observations not validated (so far):
nrow(db_resurv_RS_short_PLOT_terrestrial %>% filter(valid_1_count > 0))/
nrow(db_resurv_RS_short_PLOT_terrestrial)
[1] 0.1941721
But be aware that there are still many missing RS data, e.g. Landsat
data for bioregion CON (many points).
ggplot(db_resurv_RS_short_PLOT_terrestrial %>%
mutate(diff_GPS = if_else(
`Location method` != "Location with differential GPS" |
is.na(`Location method`), "no", "yes")),
aes(x = diff_GPS, fill = valid_1)) +
geom_bar() + labs(x = "Differential GPS")

ggplot(db_resurv_RS_short_PLOT_terrestrial %>%
mutate(GPS = case_when(
`Location method` == "Location with differential GPS" ~ "yes",
`Location method` == "Location with GPS" ~ "yes",
is.na(`Location method`) ~ "no",
TRUE ~ "no"
)),
aes(x = GPS, fill = valid_1)) +
geom_bar() + labs(x = "GPS")

Points with any rule broken and confusion between EUNIS:
nrow(db_resurv_RS_short_PLOT_terrestrial %>%
filter(EUNIS1_conf == T & valid_1_count > 0))
[1] 9
Convert to shp to look at these in GIS:
# st_write(db_resurv_RS_short_PLOT_terrestrial %>%
# filter(EUNIS1_conf == T & valid_1_count > 0) %>%
# st_as_sf(coords = c("Lon_updated", "Lat_updated"), crs = 4326),
# "C:/GIS/MOTIVATE/shapefiles/resurv_not_val_EUNIS_conf.shp")
Checked and yes
How many points with differential GPS that have at least 1 rule
broken?
nrow(db_resurv_RS_short_PLOT_terrestrial %>%
filter(`Location method` == "Location with differential GPS" &
valid_1 == "At least 1 rule broken"))
[1] 1202
Convert to shp to look at these in GIS:
# st_write(db_resurv_RS_short_PLOT_terrestrial %>%
# filter(`Location method` == "Location with differential GPS" &
# valid_1 == "At least 1 rule broken") %>%
# st_as_sf(coords = c("Lon_updated", "Lat_updated"), crs = 4326),
# "C:/GIS/MOTIVATE/shapefiles/resurv_not_val_diff_GPS.shp")
Distributions from diff GPS points without rules broken so far
Create tibble with differential GPS points without rules broken so
far:
diff_GPS_valid <- db_resurv_RS_short_PLOT_terrestrial %>%
filter(`Location method` == "Location with differential GPS" &
valid_1 == "No rules broken so far")
Max. and min. NDVI, NDMI, NDWI, SAVI and EVI
plot_distr_NDVI_max_diff_GPS_valid <- distr_plot(diff_GPS_valid,
"NDVI_max", "NDVI max")
plot_distr_NDMI_max_diff_GPS_valid <- distr_plot(diff_GPS_valid,
"NDMI_max", "NDMI max")
plot_distr_NDWI_max_diff_GPS_valid <- distr_plot(diff_GPS_valid,
"NDWI_max", "NDWI max")
plot_distr_SAVI_max_diff_GPS_valid <- distr_plot(diff_GPS_valid,
"SAVI_max", "SAVI max")
plot_distr_EVI_max_diff_GPS_valid <- distr_plot(diff_GPS_valid %>%
filter(EVI_max <= 1),
"EVI_max", "EVI max")
plot_distr_NDVI_max_diff_GPS_valid

plot_distr_NDMI_max_diff_GPS_valid

plot_distr_NDWI_max_diff_GPS_valid

plot_distr_SAVI_max_diff_GPS_valid

plot_distr_EVI_max_diff_GPS_valid

plot_distr_NDVI_min_diff_GPS_valid <- distr_plot(diff_GPS_valid,
"NDVI_min", "NDVI min")
plot_distr_NDMI_min_diff_GPS_valid <- distr_plot(diff_GPS_valid,
"NDMI_min", "NDMI min")
plot_distr_NDWI_min_diff_GPS_valid <- distr_plot(diff_GPS_valid,
"NDWI_min", "NDWI min")
plot_distr_SAVI_min_diff_GPS_valid <- distr_plot(diff_GPS_valid,
"SAVI_min", "SAVI min")
plot_distr_EVI_min_diff_GPS_valid <- distr_plot(diff_GPS_valid %>%
# Some values wrong!
# EVI should not be lower than -1
filter(EVI_min >= -1),
"EVI_min", "EVI min")
plot_distr_NDVI_min_diff_GPS_valid

plot_distr_NDMI_min_diff_GPS_valid

plot_distr_NDWI_min_diff_GPS_valid

plot_distr_SAVI_min_diff_GPS_valid

plot_distr_EVI_min_diff_GPS_valid

CH
plot_distr_CH_diff_GPS_valid <- distr_plot(diff_GPS_valid, "canopy_height",
"Canopy height (m)")
plot_distr_CH_diff_GPS_valid

Phenology
plot_distr_SOS_DOY_diff_GPS_valid <- distr_plot(diff_GPS_valid,
"SOS_DOY", "SOS DOY")
plot_distr_Peak_DOY_diff_GPS_valid <- distr_plot(diff_GPS_valid,
"Peak_DOY", "Peak DOY")
plot_distr_EOS_DOY_diff_GPS_valid <- distr_plot(diff_GPS_valid,
"EOS_DOY", "EOS DOY")
plot_distr_SOS_DOY_diff_GPS_valid

plot_distr_Peak_DOY_diff_GPS_valid

plot_distr_EOS_DOY_diff_GPS_valid

Not very conclusive…
Distributions from GPS (diff or not) points without rules broken so
far
Create tibble with differential GPS points without rules broken so
far:
all_GPS_valid <- db_resurv_RS_short_PLOT_terrestrial %>%
filter((`Location method` == "Location with differential GPS" |
`Location method` == "Location with GPS" ) &
valid_1 == "No rules broken so far")
Max. and min. NDVI, NDMI, NDWI, SAVI and EVI
plot_distr_NDVI_max_all_GPS_valid <- distr_plot(all_GPS_valid,
"NDVI_max", "NDVI max")
plot_distr_NDMI_max_all_GPS_valid <- distr_plot(all_GPS_valid,
"NDMI_max", "NDMI max")
plot_distr_NDWI_max_all_GPS_valid <- distr_plot(all_GPS_valid,
"NDWI_max", "NDWI max")
plot_distr_SAVI_max_all_GPS_valid <- distr_plot(all_GPS_valid,
"SAVI_max", "SAVI max")
plot_distr_EVI_max_all_GPS_valid <- distr_plot(all_GPS_valid %>%
filter(EVI_max <= 1),
"EVI_max", "EVI max")
plot_distr_NDVI_max_all_GPS_valid

plot_distr_NDMI_max_all_GPS_valid

plot_distr_NDWI_max_all_GPS_valid

plot_distr_SAVI_max_all_GPS_valid

plot_distr_EVI_max_all_GPS_valid

plot_distr_NDVI_min_all_GPS_valid <- distr_plot(all_GPS_valid,
"NDVI_min", "NDVI min")
plot_distr_NDMI_min_all_GPS_valid <- distr_plot(all_GPS_valid,
"NDMI_min", "NDMI min")
plot_distr_NDWI_min_all_GPS_valid <- distr_plot(all_GPS_valid,
"NDWI_min", "NDWI min")
plot_distr_SAVI_min_all_GPS_valid <- distr_plot(all_GPS_valid,
"SAVI_min", "SAVI min")
plot_distr_EVI_min_all_GPS_valid <- distr_plot(all_GPS_valid %>%
# Some values wrong!
# EVI should not be lower than -1
filter(EVI_min >= -1),
"EVI_min", "EVI min")
plot_distr_NDVI_min_all_GPS_valid

plot_distr_NDMI_min_all_GPS_valid

plot_distr_NDWI_min_all_GPS_valid

plot_distr_SAVI_min_all_GPS_valid

plot_distr_EVI_min_all_GPS_valid

CH
plot_distr_CH_all_GPS_valid <- distr_plot(all_GPS_valid, "canopy_height",
"Canopy height (m)")
plot_distr_CH_all_GPS_valid

Phenology
plot_distr_SOS_DOY_all_GPS_valid <- distr_plot(all_GPS_valid,
"SOS_DOY", "SOS DOY")
plot_distr_Peak_DOY_all_GPS_valid <- distr_plot(all_GPS_valid,
"Peak_DOY", "Peak DOY")
plot_distr_EOS_DOY_all_GPS_valid <- distr_plot(all_GPS_valid,
"EOS_DOY", "EOS DOY")
plot_distr_SOS_DOY_all_GPS_valid

plot_distr_Peak_DOY_all_GPS_valid

plot_distr_EOS_DOY_all_GPS_valid

Combined plot
grid.arrange(
plot_distr_NDVI_max_diff_GPS_valid, plot_distr_NDVI_max_all_GPS_valid,
plot_distr_NDMI_min_diff_GPS_valid, plot_distr_NDMI_min_all_GPS_valid,
ncol = 2)

Diff GPS valid points above p20 of NDVI_max and NDMI_min for each
habitat
percentiles_diff_GPS <- diff_GPS_valid %>%
group_by(EUNISa_1) %>%
summarize(percentile_20_NDVI_max = quantile(NDVI_max, probs = 0.20, na.rm = T),
percentile_20_NDMI_min = quantile(NDMI_min, probs = 0.20, na.rm = T))
diff_GPS_valid <- diff_GPS_valid %>%
left_join(percentiles_diff_GPS, by = "EUNISa_1") %>%
mutate(category_NDVI_max = case_when(
NDVI_max < percentile_20_NDVI_max ~ "below_20th",
NDVI_max >= percentile_20_NDVI_max ~ "above_20th"),
category_NDMI_min = case_when(
NDMI_min < percentile_20_NDMI_min ~ "below_20th",
NDMI_min >= percentile_20_NDMI_min ~ "above_20th"))
ggplot(data = diff_GPS_valid,
aes(x = EUNISa_1_descr, y = NDVI_max)) +
geom_flat_violin(position = position_nudge(x = 0.2, y = 0), alpha = 0.8,
fill = "lightblue") +
geom_point(aes(color = category_NDVI_max),
position = position_jitter(width = 0.15), size = 1, alpha = 0.25) +
geom_boxplot(width = 0.2, outlier.shape = NA, alpha = 0.5) +
stat_summary(fun.y = mean, geom = "point", shape = 20, size = 1) +
stat_summary(fun.data = function(x) data.frame(y = max(x) + 0.1, label = length(x)),
geom = "text", aes(label = ..label..), vjust = 0.5) +
labs(y = "NDVI max", x = "EUNIS level 1") +
guides(fill = FALSE, color = FALSE) +
scale_x_discrete(labels = function(x) str_wrap(x, width = 15)) +
scale_color_manual(values = c("below_20th" = "grey", "above_20th" = "lightblue")) +
theme_bw() + coord_flip()

ggplot(data = diff_GPS_valid,
aes(x = EUNISa_1_descr, y = NDMI_min)) +
geom_flat_violin(position = position_nudge(x = 0.2, y = 0), alpha = 0.8,
fill = "lightblue") +
geom_point(aes(color = category_NDMI_min),
position = position_jitter(width = 0.15), size = 1, alpha = 0.25) +
geom_boxplot(width = 0.2, outlier.shape = NA, alpha = 0.5) +
stat_summary(fun.y = mean, geom = "point", shape = 20, size = 1) +
stat_summary(fun.data = function(x) data.frame(y = max(x) + 0.1, label = length(x)),
geom = "text", aes(label = ..label..), vjust = 0.5) +
labs(y = "NDMI min", x = "EUNIS level 1") +
guides(fill = FALSE, color = FALSE) +
scale_x_discrete(labels = function(x) str_wrap(x, width = 15)) +
scale_color_manual(values = c("below_20th" = "grey", "above_20th" = "lightblue")) +
theme_bw() + coord_flip()

GPS (diff or not) valid points above p20 of NDVI_max and NDMI_min
for each habitat
percentiles_all_GPS <- all_GPS_valid %>%
group_by(EUNISa_1) %>%
summarize(percentile_20_NDVI_max = quantile(NDVI_max, probs = 0.20, na.rm = T),
percentile_20_NDMI_min = quantile(NDMI_min, probs = 0.20, na.rm = T))
all_GPS_valid <- all_GPS_valid %>%
left_join(percentiles_all_GPS, by = "EUNISa_1") %>%
mutate(category_NDVI_max = case_when(
NDVI_max < percentile_20_NDVI_max ~ "below_20th",
NDVI_max >= percentile_20_NDVI_max ~ "above_20th"),
category_NDMI_min = case_when(
NDMI_min < percentile_20_NDMI_min ~ "below_20th",
NDMI_min >= percentile_20_NDMI_min ~ "above_20th"))
ggplot(data = all_GPS_valid,
aes(x = EUNISa_1_descr, y = NDVI_max)) +
geom_flat_violin(position = position_nudge(x = 0.2, y = 0), alpha = 0.8,
fill = "lightblue") +
geom_point(aes(color = category_NDVI_max),
position = position_jitter(width = 0.15), size = 1, alpha = 0.25) +
geom_boxplot(width = 0.2, outlier.shape = NA, alpha = 0.5) +
stat_summary(fun.y = mean, geom = "point", shape = 20, size = 1) +
stat_summary(fun.data = function(x) data.frame(y = max(x) + 0.1, label = length(x)),
geom = "text", aes(label = ..label..), vjust = 0.5) +
labs(y = "NDVI max", x = "EUNIS level 1") +
guides(fill = FALSE, color = FALSE) +
scale_x_discrete(labels = function(x) str_wrap(x, width = 15)) +
scale_color_manual(values = c("below_20th" = "grey", "above_20th" = "lightblue")) +
theme_bw() + coord_flip()

ggplot(data = all_GPS_valid,
aes(x = EUNISa_1_descr, y = NDMI_min)) +
geom_flat_violin(position = position_nudge(x = 0.2, y = 0), alpha = 0.8,
fill = "lightblue") +
geom_point(aes(color = category_NDMI_min),
position = position_jitter(width = 0.15), size = 1, alpha = 0.25) +
geom_boxplot(width = 0.2, outlier.shape = NA, alpha = 0.5) +
stat_summary(fun.y = mean, geom = "point", shape = 20, size = 1) +
stat_summary(fun.data = function(x) data.frame(y = max(x) + 0.1, label = length(x)),
geom = "text", aes(label = ..label..), vjust = 0.5) +
labs(y = "NDMI min", x = "EUNIS level 1") +
guides(fill = FALSE, color = FALSE) +
scale_x_discrete(labels = function(x) str_wrap(x, width = 15)) +
scale_color_manual(values = c("below_20th" = "grey", "above_20th" = "lightblue")) +
theme_bw() + coord_flip()

RF models
Using the conditional inference version of random forest (cforest in
package party). Suggested if the data are highly correlated. Cforest is
more stable in deriving variable importance values in the presence of
highly correlated variables, thus providing better accuracy in
calculating variable importance (ref below).
Hothorn, T., Hornik, K. and Zeileis, A. (2006) Unbiased Recursive
Portioning: A Conditional Inference Framework. Journal of Computational
and Graphical Statistics, 15, 651- 674. http://dx.doi.org/10.1198/106186006X133933
All GPS points above p20
Filter the data to get only GPS-points above p20 of NDVI_max and
NDMI_min.
all_GPS_valid <- all_GPS_valid %>%
select(-percentile_20_NDVI_max, -percentile_20_NDMI_min)
percentiles <- all_GPS_valid %>%
group_by(EUNISa_1) %>%
summarize(
percentile_20_NDVI_max = quantile(NDVI_max, 0.20, na.rm = T),
percentile_20_NDMI_min = quantile(NDMI_min, 0.20, na.rm = T),
percentile_80_NDVI_max = quantile(NDVI_max, 0.80, na.rm = T),
percentile_80_NDMI_min = quantile(NDMI_min, 0.80, na.rm = T)
)
# Join the percentiles back to the original data
all_GPS_valid <- all_GPS_valid %>%
left_join(percentiles, by = "EUNISa_1")
# Filter rows above the 20th percentile for both variables for each category of EUNISa_1
filtered_data1 <- all_GPS_valid %>%
filter(
NDVI_max >= percentile_20_NDVI_max & NDMI_min >= percentile_20_NDMI_min
) %>%
filter(!is.na(NDVI_max) & !is.na(NDMI_max) & !is.na(NDWI_max) &
!is.na(SAVI_max) & !is.na(EVI_max) & !is.na(NDVI_min) &
!is.na(NDMI_min) & !is.na(NDWI_min) & !is.na(SAVI_min) &
!is.na(EVI_min)) %>%
mutate(EUNISa_1 = as.factor(EUNISa_1)) %>%
filter(EVI_max <= 1 & EVI_min >= -1)
Split into training and test data sets.
train_indices1 <- sample(1:nrow(filtered_data1), 0.7 * nrow(filtered_data1))
train_data1 <- filtered_data1[train_indices1, ]
test_data1 <- filtered_data1[-train_indices1, ]
Number of points per category for filtered data:
filtered_data1 %>% count(EUNISa_1)
Investigate package ggparty (e.g. autoplot function, and more).
TO-DO: Choose the hyperparameter mtry based on the square root of the
number of predictor variables (Hastie et al., 2009)-
Hastie, T., Tibshirani, R., & Friedman, J. (2009). The elements
of statistical learning: Data mining, inference, and prediction.
Springer Science & Business Media.
Maybe TO_DO: We variated ntree from 50 to 800 in steps of 50, leaving
mtry constant at 2. Tis parameter variation showed that ntree=500 was
optimal, while higher ntree led to no further model improvement
(Supplementary Fig. S10). Subsequently, the hyperparameter mtry was
varied from 2 to 8 with constant ntree=500. Here, mtry=3 led to the best
results in almost all cases (Supplementary Fig. S11). Consequently, we
chose ntree=500 and mtry=3 for our main analysis across all study
sites.
rf_cforest1 <- party::cforest(EUNISa_1 ~ NDVI_max + NDVI_min + NDMI_max +
NDMI_min + NDWI_max + NDWI_min + EVI_max +
EVI_min + SAVI_max + SAVI_min + canopy_height,
data = train_data1,
controls = cforest_control(
mtry = 3,
# mtry = sqrt(11)
# Default mtry = 5
# Bagging: mtry = NULL
# or = number of input variables
ntree = 500) # Default, try increasing
)
predictions_rf_cforest1 <- predict(rf_cforest1, newdata = test_data1,
OOB = TRUE, type = "response")
Confusion matrix:
confusionMatrix(predictions_rf_cforest1, test_data1$EUNISa_1)
Confusion Matrix and Statistics
Reference
Prediction Q R S T
Q 638 327 0 0
R 260 1491 1 0
S 0 0 38 4
T 0 0 12 56
Overall Statistics
Accuracy : 0.7863
95% CI : (0.7708, 0.8013)
No Information Rate : 0.6431
P-Value [Acc > NIR] : < 2.2e-16
Kappa : 0.566
Mcnemar's Test P-Value : NA
Statistics by Class:
Class: Q Class: R Class: S Class: T
Sensitivity 0.7105 0.8201 0.74510 0.93333
Specificity 0.8305 0.7413 0.99856 0.99566
Pos Pred Value 0.6611 0.8510 0.90476 0.82353
Neg Pred Value 0.8604 0.6958 0.99533 0.99855
Prevalence 0.3177 0.6431 0.01804 0.02122
Detection Rate 0.2257 0.5274 0.01344 0.01981
Detection Prevalence 0.3414 0.6197 0.01486 0.02405
Balanced Accuracy 0.7705 0.7807 0.87183 0.96450
SurrogateTree –> does not work
varimp_rf_cforest1 <- party::varimp(rf_cforest1, conditional = F)
# conditional = T adjusts for correlations between predictor variables
# Takes long!
# party::varimp(rf_cforest1, conditional = T)
# conditional = T adjusts for correlations between predictor variables
# Takes long!
Variable Importance Plot
varimp_rf_cforest1_df <- data.frame(Variable = names(varimp_rf_cforest1),
Importance = varimp_rf_cforest1)
ggplot(varimp_rf_cforest1_df,
aes(x = reorder(Variable, Importance), y = Importance)) +
geom_bar(stat = "identity", fill = "lightblue") +
coord_flip() + theme_minimal() +
labs(title = "Variable Importance", x = "Variables", y = "Importance")

Tree Visualization
# Create a single conditional inference tree using ctree
single_tree1 <- ctree(EUNISa_1 ~ NDVI_max + NDVI_min + NDMI_max + NDMI_min +
NDWI_max + NDWI_min + EVI_max + EVI_min + SAVI_max +
SAVI_min + canopy_height,
data = train_data1)
# Plot the single tree using
autoplot(single_tree1)

All GPS points within IQ range
Filter the data to get only GPS-points within IQ range of NDVI_max
and NDMI_min.
IQ_ranges <- all_GPS_valid %>%
group_by(EUNISa_1) %>%
summarize(
Q1_NDVI_max = quantile(NDVI_max, 0.25, na.rm = T),
Q1_NDMI_min = quantile(NDMI_min, 0.25, na.rm = T),
Q3_NDVI_max = quantile(NDVI_max, 0.75, na.rm = T),
Q3_NDMI_min = quantile(NDMI_min, 0.75, na.rm = T)
)
# Join the IQ ranges back to the original data
all_GPS_valid <- all_GPS_valid %>%
left_join(IQ_ranges, by = "EUNISa_1")
# Filter rows within the IQR range for both variables
filtered_data2 <- all_GPS_valid %>%
filter(
(NDVI_max >= Q1_NDVI_max & NDVI_max <= Q3_NDVI_max) &
(NDMI_min >= Q1_NDMI_min & NDMI_min <= Q3_NDMI_min)
) %>%
filter(!is.na(NDVI_max) & !is.na(NDMI_max) & !is.na(NDWI_max) &
!is.na(SAVI_max) & !is.na(EVI_max) & !is.na(NDVI_min) &
!is.na(NDMI_min) & !is.na(NDWI_min) & !is.na(SAVI_min) &
!is.na(EVI_min)) %>%
mutate(EUNISa_1 = as.factor(EUNISa_1)) %>%
filter(EVI_max <= 1 & EVI_min >= -1)
Split into training and test data sets.
train_indices2 <- sample(1:nrow(filtered_data2), 0.7 * nrow(filtered_data2))
train_data2 <- filtered_data2[train_indices2, ]
test_data2 <- filtered_data2[-train_indices2, ]
Number of points per category for filtered data:
filtered_data2 %>% count(EUNISa_1)
rf_cforest2 <- party::cforest(EUNISa_1 ~ NDVI_max + NDVI_min + NDMI_max +
NDMI_min + NDWI_max + NDWI_min + EVI_max +
EVI_min + SAVI_max + SAVI_min + canopy_height,
data = train_data2,
controls = cforest_control(
mtry = 3,
# mtry = sqrt(11)
# Default mtry = 5
# Bagging: mtry = NULL
# or = number of input variables
ntree = 500) # Default, try increasing
)
predictions_rf_cforest2 <- predict(rf_cforest2, newdata = test_data2,
OOB = TRUE, type = "response")
Confusion matrix:
confusionMatrix(predictions_rf_cforest2, test_data2$EUNISa_1)
Confusion Matrix and Statistics
Reference
Prediction Q R S T
Q 310 5 0 0
R 43 885 0 0
S 0 0 14 2
T 0 0 6 22
Overall Statistics
Accuracy : 0.9565
95% CI : (0.9439, 0.967)
No Information Rate : 0.6915
P-Value [Acc > NIR] : < 2.2e-16
Kappa : 0.8997
Mcnemar's Test P-Value : NA
Statistics by Class:
Class: Q Class: R Class: S Class: T
Sensitivity 0.8782 0.9944 0.70000 0.91667
Specificity 0.9946 0.8917 0.99842 0.99525
Pos Pred Value 0.9841 0.9537 0.87500 0.78571
Neg Pred Value 0.9558 0.9861 0.99528 0.99841
Prevalence 0.2743 0.6915 0.01554 0.01865
Detection Rate 0.2409 0.6876 0.01088 0.01709
Detection Prevalence 0.2448 0.7211 0.01243 0.02176
Balanced Accuracy 0.9364 0.9430 0.84921 0.95596
varimp_rf_cforest2 <- party::varimp(rf_cforest2, conditional = F)
Variable Importance Plot
varimp_rf_cforest2_df <- data.frame(Variable = names(varimp_rf_cforest2),
Importance = varimp_rf_cforest2)
ggplot(varimp_rf_cforest2_df,
aes(x = reorder(Variable, Importance), y = Importance)) +
geom_bar(stat = "identity", fill = "lightblue") +
coord_flip() + theme_minimal() +
labs(title = "Variable Importance", x = "Variables", y = "Importance")

All GPS points within mean +/- SD
Filter the data to get only GPS-points within mean +/- SD of NDVI_max
and NDMI_min.
mean_sd <- all_GPS_valid %>%
group_by(EUNISa_1) %>%
summarize(
mean_NDVI_max = mean(all_GPS_valid$NDVI_max, na.rm = T),
mean_NDMI_min = mean(all_GPS_valid$NDMI_min, na.rm = T),
sd_NDVI_max = sd(all_GPS_valid$NDVI_max, na.rm = T),
sd_NDMI_min = sd(all_GPS_valid$NDMI_min, na.rm = T)
)
# Join the IQ ranges back to the original data
all_GPS_valid <- all_GPS_valid %>%
left_join(mean_sd, by = "EUNISa_1")
# Filter rows within the specified range for both variables
filtered_data3 <- all_GPS_valid %>%
filter(
(NDVI_max >= (mean_NDVI_max - sd_NDVI_max) & NDVI_max <= (mean_NDVI_max + sd_NDVI_max)) &
(NDMI_min >= (mean_NDMI_min - sd_NDMI_min) & NDMI_min <= (mean_NDMI_min + sd_NDMI_min))
) %>%
filter(!is.na(NDVI_max) & !is.na(NDMI_max) & !is.na(NDWI_max) &
!is.na(SAVI_max) & !is.na(EVI_max) & !is.na(NDVI_min) &
!is.na(NDMI_min) & !is.na(NDWI_min) & !is.na(SAVI_min) &
!is.na(EVI_min)) %>%
mutate(EUNISa_1 = as.factor(EUNISa_1)) %>%
filter(EVI_max <= 1 & EVI_min >= -1)
Split into training and test data sets.
train_indices3 <- sample(1:nrow(filtered_data3), 0.7 * nrow(filtered_data3))
train_data3 <- filtered_data3[train_indices3, ]
test_data3 <- filtered_data3[-train_indices3, ]
Number of points per category for filtered data:
filtered_data3 %>% count(EUNISa_1)
rf_cforest3 <- party::cforest(EUNISa_1 ~ NDVI_max + NDVI_min + NDMI_max +
NDMI_min + NDWI_max + NDWI_min + EVI_max +
EVI_min + SAVI_max + SAVI_min + canopy_height,
data = train_data3,
controls = cforest_control(
mtry = 3,
# mtry = sqrt(11)
# Default mtry = 5
# Bagging: mtry = NULL
# or = number of input variables
ntree = 500) # Default, try increasing
)
predictions_rf_cforest3 <- predict(rf_cforest3, newdata = test_data3,
OOB = TRUE, type = "response")
Confusion matrix:
confusionMatrix(predictions_rf_cforest3, test_data3$EUNISa_1)
Confusion Matrix and Statistics
Reference
Prediction Q R S T
Q 264 132 0 0
R 511 1283 0 0
S 0 0 36 2
T 0 0 13 53
Overall Statistics
Accuracy : 0.7132
95% CI : (0.6942, 0.7316)
No Information Rate : 0.6168
P-Value [Acc > NIR] : < 2.2e-16
Kappa : 0.3741
Mcnemar's Test P-Value : NA
Statistics by Class:
Class: Q Class: R Class: S Class: T
Sensitivity 0.3406 0.9067 0.73469 0.96364
Specificity 0.9131 0.4187 0.99911 0.99419
Pos Pred Value 0.6667 0.7152 0.94737 0.80303
Neg Pred Value 0.7308 0.7360 0.99424 0.99910
Prevalence 0.3378 0.6168 0.02136 0.02398
Detection Rate 0.1151 0.5593 0.01569 0.02310
Detection Prevalence 0.1726 0.7820 0.01656 0.02877
Balanced Accuracy 0.6269 0.6627 0.86690 0.97892
varimp_rf_cforest3 <- party::varimp(rf_cforest3, conditional = F)
Variable Importance Plot
varimp_rf_cforest3_df <- data.frame(Variable = names(varimp_rf_cforest3),
Importance = varimp_rf_cforest3)
ggplot(varimp_rf_cforest3_df,
aes(x = reorder(Variable, Importance), y = Importance)) +
geom_bar(stat = "identity", fill = "lightblue") +
coord_flip() + theme_minimal() +
labs(title = "Variable Importance", x = "Variables", y = "Importance")

All GPS points above p20 and below p80
Filter the data to get only GPS-points above p20 and below p80 of
NDVI_max and NDMI_min.
# Filter rows above the 20th percentile and below the 80th percentile for both variables
filtered_data4 <- all_GPS_valid %>%
filter(
(NDVI_max >= percentile_20_NDVI_max & NDVI_max <= percentile_80_NDVI_max) &
(NDMI_min >= percentile_20_NDMI_min & NDMI_min <= percentile_80_NDMI_min)
) %>%
filter(!is.na(NDVI_max) & !is.na(NDMI_max) & !is.na(NDWI_max) &
!is.na(SAVI_max) & !is.na(EVI_max) & !is.na(NDVI_min) &
!is.na(NDMI_min) & !is.na(NDWI_min) & !is.na(SAVI_min) &
!is.na(EVI_min)) %>%
mutate(EUNISa_1 = as.factor(EUNISa_1)) %>%
filter(EVI_max <= 1 & EVI_min >= -1)
Split into training and test data sets.
train_indices4 <- sample(1:nrow(filtered_data4), 0.7 * nrow(filtered_data4))
train_data4 <- filtered_data4[train_indices4, ]
test_data4 <- filtered_data4[-train_indices4, ]
Number of points per category for filtered data:
filtered_data4 %>% count(EUNISa_1)
rf_cforest4 <- party::cforest(EUNISa_1 ~ NDVI_max + NDVI_min + NDMI_max +
NDMI_min + NDWI_max + NDWI_min + EVI_max +
EVI_min + SAVI_max + SAVI_min + canopy_height,
data = train_data4,
controls = cforest_control(
mtry = 3,
# mtry = sqrt(11)
# Default mtry = 5
# Bagging: mtry = NULL
# or = number of input variables
ntree = 500) # Default, try increasing
)
predictions_rf_cforest4 <- predict(rf_cforest4, newdata = test_data4,
OOB = TRUE, type = "response")
Confusion matrix:
confusionMatrix(predictions_rf_cforest4, test_data4$EUNISa_1)
Confusion Matrix and Statistics
Reference
Prediction Q R S T
Q 387 30 0 0
R 149 1121 0 0
S 0 0 17 1
T 0 0 11 30
Overall Statistics
Accuracy : 0.8906
95% CI : (0.875, 0.9049)
No Information Rate : 0.6592
P-Value [Acc > NIR] : < 2.2e-16
Kappa : 0.7551
Mcnemar's Test P-Value : NA
Statistics by Class:
Class: Q Class: R Class: S Class: T
Sensitivity 0.7220 0.9739 0.607143 0.96774
Specificity 0.9752 0.7496 0.999418 0.99359
Pos Pred Value 0.9281 0.8827 0.944444 0.73171
Neg Pred Value 0.8879 0.9370 0.993634 0.99941
Prevalence 0.3070 0.6592 0.016037 0.01775
Detection Rate 0.2216 0.6420 0.009737 0.01718
Detection Prevalence 0.2388 0.7274 0.010309 0.02348
Balanced Accuracy 0.8486 0.8618 0.803280 0.98066
HERE: Compare RF 1-4
Cordillera data
AlpineGrasslands_indices <- read_csv(
"C:/Data/MOTIVATE/Cordillera/AlpineGrasslands/AlpineGrassland_Sentinel_Plot_Allyear_Allmetrics.csv")
Rows: 40 Columns: 69── Column specification ─────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (9): system:index, Codigo, H�bitat, Localidad, Precisi�n, Subregion, UTM, source, .geo
dbl (60): Altitude__, Altura_med, Altura_m�, Cobertur_1, Cobertur_2, Cobertura, DATE, EVI_max, EV...
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
AlpineGrasslands_phen <- read_csv(
"C:/Data/MOTIVATE/Cordillera/AlpineGrasslands/AlpineGrasslands_Phenology_SOS_EOS_Peak_NDVI_Amplitude.csv")
Rows: 40 Columns: 34── Column specification ─────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (8): system:index, Codigo, H�bitat, Localidad, Precisi�n, Subregion, UTM, .geo
dbl (26): Altitude__, Altura_med, Altura_m�, Cobertur_1, Cobertur_2, Cobertura, DATE, EOS_DOY, Gp...
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
AlpineGrasslands_CH <- read_csv(
"C:/Data/MOTIVATE/Cordillera/AlpineGrasslands/AlpineGrasslands_CanopyHeight_1m.csv")
Rows: 40 Columns: 27── Column specification ─────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (8): system:index, Codigo, Hábitat, Localidad, Precisión, Subregion, UTM, .geo
dbl (19): Altitude__, Altura_med, Altura_má, Cobertur_1, Cobertur_2, Cobertura, Date__year, Gps_e...
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
VegetationTypes_indices <- read_csv(
"C:/Data/MOTIVATE/Cordillera/VegetationTypes/VegetationTypes_Sentinel_Plot_AllYear_Allmetrics.csv")
Rows: 32 Columns: 54── Column specification ─────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (8): system:index, EUNIS, ID, TYPE, Xo, Yo, source, .geo
dbl (45): EVI_max, EVI_median, EVI_min, EVI_mode, EVI_p10, EVI_p90, EVI_stdDev, EVImean, NDMI_ma...
date (1): DATE
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
VegetationTypes_phen <- read_csv(
"C:/Data/MOTIVATE/Cordillera/VegetationTypes/VegetationTypes_Phenology_SOS_EOS_Peak_NDVI_Amplitude.csv")
Rows: 32 Columns: 19── Column specification ─────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (7): system:index, EUNIS, ID, TYPE, Xo, Yo, .geo
dbl (11): EOS_DOY, NDVI_Amplitude, NDVI_at_EOS, NDVI_at_Peak, NDVI_at_SOS, Peak_DOY, SOS_DOY, Se...
date (1): DATE
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
VegetationTypes_CH <- read_csv(
"C:/Data/MOTIVATE/Cordillera/VegetationTypes/VegetationTypes_CanopyHeight_1m.csv")
Rows: 32 Columns: 12── Column specification ─────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (7): system:index, EUNIS, ID, TYPE, Xo, Yo, .geo
dbl (4): X, Y, canopy_height, evaluated
date (1): DATE
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
AlpineGrasslands <- AlpineGrasslands_indices %>%
select(-`system:index`, -.geo, -Localidad) %>%
rename(Hábitat = "H�bitat") %>%
full_join(AlpineGrasslands_phen %>%
select(-`system:index`, -.geo, -Localidad) %>%
rename(Hábitat = "H�bitat")) %>%
full_join(AlpineGrasslands_CH %>%
select(-`system:index`, -.geo, -Localidad)) %>%
select(-Date__year, - `Precisi�n`) %>%
mutate(DATE = ymd(DATE)) %>%
rename(ID = "Releve_num") %>%
mutate(ID = as.character(ID)) %>%
mutate(layer = "AlpineGrasslands")
Joining with `by = join_by(Altitude__, Altura_med, `Altura_m�`, Cobertur_1, Cobertur_2, Cobertura, Codigo, DATE, Gps_error, Hgtavg_hl, Hgtmax_hl, Hábitat, Latitude, Longitude, Num_specie, Orientaci, Pendiente, `Precisi�n`, Releve_num, Subregion, UTM, X, Y)`Joining with `by = join_by(Altitude__, Altura_med, Cobertur_1, Cobertur_2, Cobertura, Codigo, Gps_error, Hgtavg_hl, Hgtmax_hl, Hábitat, Latitude, Longitude, Num_specie, Orientaci, Pendiente, Releve_num, Subregion, UTM, X, Y)`
VegetationTypes <- VegetationTypes_indices %>%
select(-`system:index`, -.geo) %>%
full_join(VegetationTypes_phen %>%
select(-`system:index`, -.geo)) %>%
full_join(VegetationTypes_CH %>%
select(-`system:index`, -.geo)) %>%
rename(Hábitat = "TYPE") %>%
mutate(layer = "VegetationTypes")
Joining with `by = join_by(DATE, EUNIS, ID, TYPE, X, Xo, Y, Yo, evaluated)`Joining with `by = join_by(DATE, EUNIS, ID, TYPE, X, Xo, Y, Yo, evaluated)`
Merge both datasets:
cordillera <- bind_rows(
AlpineGrasslands %>% select(DATE, ID, starts_with("NDMI"),
starts_with("NDVI"), Hábitat, "EOS_DOY",
"Peak_DOY", "SOS_DOY", "Season_Length",
"canopy_height", "layer"),
VegetationTypes %>% select(DATE, ID, starts_with("NDMI"),
starts_with("NDVI"), Hábitat, "EOS_DOY",
"Peak_DOY", "SOS_DOY", "Season_Length",
"canopy_height", "layer")
) %>%
mutate(EUNISa_1 = case_when(
Hábitat = str_detect(Hábitat, "Pastizal|Cervunal|grassland|meadow") ~ "R",
Hábitat = str_detect(Hábitat, "forest") ~ "T",
Hábitat = str_detect(Hábitat, "Scrub|scrub|Shrubland|shrubland|shrub|Heathland") ~ "S",
Hábitat = str_detect(Hábitat, "Suelo|Scree|scree|cliff") ~ "U",
Hábitat = is.na(Hábitat) ~ "R",
TRUE ~ NA_character_),
EUNISa_1_descr = case_when(
EUNISa_1 == "R" ~ "Grasslands",
EUNISa_1 == "T" ~ "Forests and other wooded land",
EUNISa_1 == "S" ~ "Heathlands, scrub and tundra",
EUNISa_1 == "U" ~ "Inland habitats with no or little soil")
)
Max. and min. NDVI, NDMI
plot_distr_NDVI_max_cordi <- distr_plot(cordillera,
"NDVI_max", "NDVI max")
plot_distr_NDVI_min_cordi <- distr_plot(cordillera,
"NDVI_min", "NDVI min")
plot_distr_NDMI_max_cordi <- distr_plot(cordillera,
"NDMI_max", "NDMI max")
plot_distr_NDMI_min_cordi <- distr_plot(cordillera,
"NDMI_min", "NDMI min")
plot_distr_NDVI_max_cordi

plot_distr_NDVI_min_cordi

plot_distr_NDMI_max_cordi

plot_distr_NDMI_min_cordi

OLD from here
Plots NDVI and NDMI
ggplot(data = cordi %>% filter(!is.na(NDVI_max)) %>%
# Keep only forests, grasslands, shrublands and wetlands
filter(EUNIS_1 %in% c("T", "R", "S", "Q")),
aes(x = EUNIS_1, y = NDVI_max, fill = EUNIS_1)) +
geom_flat_violin(position = position_nudge(x = 0.2, y = 0), alpha = 0.8) +
geom_point(aes(y = NDVI_max, color = EUNIS_1),
position = position_jitter(width = 0.15), size = 3,
alpha = 0.5) +
geom_boxplot(width = 0.2, outlier.shape = NA, alpha = 0.5) +
stat_summary(fun.y=mean, geom="point", shape = 20, size = 3) +
stat_summary(fun.data = function(x) data.frame(y = max(x) + 0.1,
label = length(x)),
geom = "text", aes(label = ..label..), vjust = 0.5) +
guides(fill = FALSE, color = FALSE) + theme_bw() + coord_flip()
Error: objeto 'cordi' no encontrado
Maps
Points differential GPS
Maps
Number of differential GPS points by Country:
Points ReSurvey
Maps
Number of ReSurvey points by Country:
Join
Session info
LS0tDQp0aXRsZTogIlNjcmlwdCB0byB2YWxpZGF0ZSBwb2ludHMgaW4gUmVTdXJ2ZXkgZGF0YWJhc2UgdXNpbmcgUlMgZGF0YSINCnN1YnRpdGxlOiAiQWRkaW5nIEFUTF9CRU5FTFVYIGFuZCBDT05fTk9SRElDIFMyIGRhdGEsIHNvbWUgTGFuZHNhdCBkYXRhLCBhZGRpbmcgY2Fub3B5IGhlaWdodCBkYXRhIg0KYXV0aG9yOiAiQWxpY2lhIFZhbGTDqXMiDQpkYXRlOiAiYHIgZm9ybWF0KFN5cy50aW1lKCksICclZCAlQiAlWScpYCINCm91dHB1dDoNCiAgcGRmX2RvY3VtZW50OiBkZWZhdWx0DQogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQNCi0tLQ0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldCh3YXJuaW5nID0gRkFMU0UpDQpgYGANCg0KVGhpcyBSIHNjcmlwdCBpcyB1c2VkIHRvIHZhbGlkYXRlIHRoZSBwb2ludHMgaW4gdGhlIFJlU3VydmV5IGRhdGFiYXNlIHVzaW5nIFJTIGluZGljYXRvcnMgKE5EVkksIE5ETUksIGNhbm9weSBoZWlnaHQpLg0KDQojIExvYWQgbGlicmFyaWVzDQoNCmBgYHtyfQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KGhlcmUpDQpsaWJyYXJ5KGdyaWRFeHRyYSkNCmxpYnJhcnkocmVhZHhsKQ0KbGlicmFyeShzY2FsZXMpDQpsaWJyYXJ5KHNmKQ0KbGlicmFyeShybmF0dXJhbGVhcnRoKQ0KbGlicmFyeShkdHBseXIpDQpsaWJyYXJ5KGxtZTQpDQpsaWJyYXJ5KGxtZXJUZXN0KQ0KbGlicmFyeShjYXIpDQpsaWJyYXJ5KGdnZWZmZWN0cykNCmxpYnJhcnkocGFydHkpDQpsaWJyYXJ5KHBhcnR5a2l0KQ0KbGlicmFyeShzdHJ1Y2NoYW5nZSkNCmxpYnJhcnkoZ2dwYXJ0eSkNCmxpYnJhcnkoY2FyZXQpDQpsaWJyYXJ5KG1vcmVwYXJ0eSkNCmxpYnJhcnkocmFuZG9tRm9yZXN0KQ0KYGBgDQoNCiMgRGVmaW5lIHByaW50YWxsIGZ1bmN0aW9uDQoNCmBgYHtyfQ0KcHJpbnRhbGwgPC0gZnVuY3Rpb24odGliYmxlKSB7DQogIHByaW50KHRpYmJsZSwgd2lkdGggPSBJbmYpDQogIH0NCmBgYA0KDQojIExvYWQgZ2VvbV9mbGF0X3Zpb2xpbiBwbG90DQoNCmBgYHtyfQ0Kc291cmNlKCJodHRwczovL2dpc3QuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2Jlbm1hcndpY2svMmExYmIwMTMzZmY1NjhjYmUyOGQvcmF3L2ZiNTNiZDk3MTIxZjdmOWNlOTQ3ODM3ZWYxYTRjNjVhNzNiZmZiM2YvZ2VvbV9mbGF0X3Zpb2xpbi5SIikNCmBgYA0KDQojIFJlYWQgUmVTdXJ2ZXkgZGF0YSB3aXRoIFJTIGluZGljYXRvcnMNCg0KYGBge3J9DQpkYl9yZXN1cnZfUlM8LXJlYWRfdHN2KA0KICBoZXJlKCJkYXRhIiwgImNsZWFuIiwgImRiX3Jlc3Vydl9SU18yMDI1MDUwNS5jc3YiKSwNCiAgY29sX3R5cGVzID0gY29scygNCiAgICAjIER5bmFtaWNhbGx5IHNwZWNpZnkgRVVOSVMgY29sdW1ucyBhcyBjaGFyYWN0ZXINCiAgICAuZGVmYXVsdCA9IGNvbF9ndWVzcygpLCAgIyBEZWZhdWx0IGd1ZXNzaW5nIGZvciBvdGhlciBjb2x1bW5zDQogICAgRVVOSVNhID0gY29sX2NoYXJhY3RlcigpLA0KICAgIEVVTklTYiA9IGNvbF9jaGFyYWN0ZXIoKSwNCiAgICBFVU5JU2MgPSBjb2xfY2hhcmFjdGVyKCksDQogICAgRVVOSVNkID0gY29sX2NoYXJhY3RlcigpLA0KICAgIEVVTklTYV8xID0gY29sX2NoYXJhY3RlcigpLA0KICAgIEVVTklTYV8yID0gY29sX2NoYXJhY3RlcigpLA0KICAgIEVVTklTYV8zID0gY29sX2NoYXJhY3RlcigpLA0KICAgIEVVTklTYV80ID0gY29sX2NoYXJhY3RlcigpLA0KICAgIEVVTklTYl8xID0gY29sX2NoYXJhY3RlcigpLA0KICAgIEVVTklTYl8yID0gY29sX2NoYXJhY3RlcigpLA0KICAgIEVVTklTYl8zID0gY29sX2NoYXJhY3RlcigpLA0KICAgIEVVTklTYl80ID0gY29sX2NoYXJhY3RlcigpLA0KICAgIEVVTklTY18xID0gY29sX2NoYXJhY3RlcigpLA0KICAgIEVVTklTY18yID0gY29sX2NoYXJhY3RlcigpLA0KICAgIEVVTklTY18zID0gY29sX2NoYXJhY3RlcigpLA0KICAgIEVVTklTY180ID0gY29sX2NoYXJhY3RlcigpLA0KICAgIEVVTklTZF8xID0gY29sX2NoYXJhY3RlcigpLA0KICAgIEVVTklTZF8yID0gY29sX2NoYXJhY3RlcigpLA0KICAgIEVVTklTZF8zID0gY29sX2NoYXJhY3RlcigpLA0KICAgIEVVTklTZF80ID0gY29sX2NoYXJhY3RlcigpLA0KICAgIEVVTklTYV8xX2Rlc2NyID0gY29sX2NoYXJhY3RlcigpLA0KICAgIEVVTklTYl8xX2Rlc2NyID0gY29sX2NoYXJhY3RlcigpLA0KICAgIEVVTklTY18xX2Rlc2NyID0gY29sX2NoYXJhY3RlcigpLA0KICAgIEVVTklTZF8xX2Rlc2NyID0gY29sX2NoYXJhY3RlcigpLA0KICAgIEVVTklTX2Fzc2lnbmF0aW9uID0gY29sX2NoYXJhY3RlcigpLA0KICAgIEVVTklTYV8yX2Rlc2NyID0gY29sX2NoYXJhY3RlcigpLA0KICAgIEVVTklTYV8zX2Rlc2NyID0gY29sX2NoYXJhY3RlcigpLA0KICAgIEVVTklTYV80X2Rlc2NyID0gY29sX2NoYXJhY3RlcigpLA0KICAgIEVVTklTYl8yX2Rlc2NyID0gY29sX2NoYXJhY3RlcigpLA0KICAgIEVVTklTYl8zX2Rlc2NyID0gY29sX2NoYXJhY3RlcigpLA0KICAgIEVVTklTYl80X2Rlc2NyID0gY29sX2NoYXJhY3RlcigpLA0KICAgIEVVTklTY18yX2Rlc2NyID0gY29sX2NoYXJhY3RlcigpLA0KICAgIEVVTklTY18zX2Rlc2NyID0gY29sX2NoYXJhY3RlcigpLA0KICAgIEVVTklTY180X2Rlc2NyID0gY29sX2NoYXJhY3RlcigpLA0KICAgIEVVTklTZF8yX2Rlc2NyID0gY29sX2NoYXJhY3RlcigpLA0KICAgIEVVTklTZF8zX2Rlc2NyID0gY29sX2NoYXJhY3RlcigpLA0KICAgIEVVTklTZF80X2Rlc2NyID0gY29sX2NoYXJhY3RlcigpDQogICAgKQ0KICApDQpgYGANCg0KTm8gcGFyc2luZyBpc3N1ZXMhDQoNCiMgU29tZSBkYXRhIG1hbmFnZW5lbXQNCg0KIyMgU2V2ZXJhbCBFVU5JUyBsZXZlbCAxIGFzc2lnbmVkDQoNCk51bWJlciBvZiByb3dzIHdoZXJlIHRoZXJlIGlzIG1vcmUgdGhhbiBvbmUgRVVOSVMgMSBhc3NpZ25lZCwgYW5kIHRoZXkgYXJlIGRpZmZlcmVudCBhbW9uZyB0aGVtLiBTZWUgd2hhdCB0byBkbyB3aXRoIHRoZXNlIGxhdGVyISBTbyBmYXIgSSB0YWtlIEVVTklTYV8xLg0KDQpgYGB7cn0NCm5yb3coZGJfcmVzdXJ2X1JTICU+JSANCiAgICAgICAjIFJvd3Mgd2l0aCBtb3JlIHRoYW4gb25lIEVVTklTIDEgYXNzaWduZWQNCiAgICAgICBmaWx0ZXIoIWlzLm5hKEVVTklTYl8xKSkgJT4lIA0KICAgICAgIGZpbHRlcihFVU5JU2FfMSE9RVVOSVNiXzEgfCBFVU5JU2JfMSAhPSBFVU5JU2NfMSB8IEVVTklTYV8xICE9IEVVTklTY18xKSkNCmBgYA0KDQpTZWUgImNvbmZ1c2lvbnMiOg0KDQpgYGB7cn0NCmRiX3Jlc3Vydl9SUyAlPiUgDQogICMgUm93cyB3aXRoIG1vcmUgdGhhbiBvbmUgRVVOSVMgMSBhc3NpZ25lZA0KICBmaWx0ZXIoIWlzLm5hKEVVTklTYl8xKSkgJT4lIA0KICBmaWx0ZXIoRVVOSVNhXzEhPUVVTklTYl8xIHwgRVVOSVNiXzEgIT0gRVVOSVNjXzEgfCBFVU5JU2FfMSAhPSBFVU5JU2NfMSkgJT4lDQogIGRpc3RpbmN0KEVVTklTYV8xLCBFVU5JU2JfMSwgRVVOSVNjXzEsIEVVTklTZF8xKQ0KYGBgDQoNCkRlZmluZSAiY29uZnVzaW9uIiBjb2x1bW5zOg0KDQpgYGB7cn0NCmRiX3Jlc3Vydl9SUyA8LSBkYl9yZXN1cnZfUlMgJT4lDQogIG11dGF0ZShFVU5JUzFfY29uZl90eXBlID0gY2FzZV93aGVuKA0KICAgIEVVTklTYV8xID09ICJSIiAmIEVVTklTYl8xID09ICJTIiB+ICJSL1MiLA0KICAgIEVVTklTYV8xID09ICJTIiAmIEVVTklTYl8xID09ICJUIiB+ICJTL1QiLA0KICAgIEVVTklTYV8xID09ICJSIiAmIEVVTklTYl8xID09ICJSIiAmIEVVTklTY18xID09ICJTIiB+ICJSL1MiLA0KICAgIEVVTklTYV8xID09ICJSIiAmIEVVTklTYl8xID09ICJSIiAmIEVVTklTY18xID09ICJTIiAmIEVVTklTZF8xID09ICJTIiB+ICJSL1MiLA0KICAgIEVVTklTYV8xID09ICJQIiAmIEVVTklTYl8xID09ICJRIiB+ICJQL1EiLA0KICAgIFRSVUUgfiBOQV9jaGFyYWN0ZXJfKSwNCiAgICBFVU5JUzFfY29uZiA9ICFpcy5uYShFVU5JUzFfY29uZl90eXBlKSkNCmBgYA0KDQoNCiMjIFRpYmJsZSB3aXRoIHNlbGVjdGVkIGNvbHVtbnMNCg0KYGBge3J9DQpkYl9yZXN1cnZfUlNfc2hvcnQgPC0gZGJfcmVzdXJ2X1JTICU+JQ0KICBzZWxlY3QoUGxvdE9ic2VydmF0aW9uSUQsIENvdW50cnksIFJTX0NPREUsIGBSZVN1cnZleSBzaXRlYCwgYFJlU3VydmV5IHBsb3RgLA0KICAgICAgICAgYE1hbmlwdWxhdGUgKHkvbilgLCBgVHlwZSBvZiBtYW5pcHVsYXRpb25gLCBMb25fdXBkYXRlZCwgTGF0X3VwZGF0ZWQsDQogICAgICAgICBgTG9jYXRpb24gbWV0aG9kYCwgYExvY2F0aW9uIHVuY2VydGFpbnR5IChtKWAsIEVVTklTYV8xLA0KICAgICAgICAgRVVOSVNhXzFfZGVzY3IsIEVVTklTYV8yLCBFVU5JU2FfMl9kZXNjciwgRVVOSVNhXzMsIEVVTklTYV8zX2Rlc2NyLA0KICAgICAgICAgRVVOSVNhXzQsIEVVTklTYV80X2Rlc2NyLCBFVU5JUzFfY29uZiwgRVVOSVMxX2NvbmZfdHlwZSwNCiAgICAgICAgIGRhdGUsIHllYXIsIGJpb2dlbywgdW5pdCwgeWVhcl9SUywgTG9uX1JTLCBMYXRfUlMsIA0KICAgICAgICAgc3RhcnRzX3dpdGgoIk5EVkkiKSwgc3RhcnRzX3dpdGgoIk5ETUkiKSwgc3RhcnRzX3dpdGgoIk5EV0kiKSwNCiAgICAgICAgIHN0YXJ0c193aXRoKCJFVkkiKSwgc3RhcnRzX3dpdGgoIlNBVkkiKSwgY2Fub3B5X2hlaWdodCwNCiAgICAgICAgIFNPU19ET1ksIFNPU19kYXRlLCBORFZJX2F0X1NPUywgUGVha19ET1ksIFBlYWtfZGF0ZSwgTkRWSV9hdF9QZWFrLA0KICAgICAgICAgRU9TX0RPWSwgRU9TX2RhdGUsIE5EVklfYXRfRU9TLCBTZWFzb25fTGVuZ3RoLA0KICAgICAgICAgUzJfZGF0YSwgTGFuZHNhdF9kYXRhLCBDSF9kYXRhLCBTMl9waGVuX2RhdGEpDQpgYGANCg0KIyMgVE8tRE86IE1pc3NpbmcgZGF0YSBjaGVja3MNCg0KRG8gd2hlbiBhbGwgUlMgZGF0YSBpcyByZWFkeSENCg0KIyMgRmxhZyB3aGVuIHllYXIgaXMgZGlmZmVyZW50IGJldHdlZW4gUlMgZGF0YSBhbmQgUmVTdXJ2ZXkgZGINCg0KYGBge3J9DQpkYl9yZXN1cnZfUlNfc2hvcnQgPC0gZGJfcmVzdXJ2X1JTX3Nob3J0ICU+JQ0KICBtdXRhdGUoeWVhcl9kaWZmID0geWVhciAhPSB5ZWFyX1JTKQ0KYGBgDQoNCmBgYHtyfQ0KZGJfcmVzdXJ2X1JTX3Nob3J0ICU+JSBjb3VudCh5ZWFyX2RpZmYpDQpgYGANCg0KMiB3aXRoIGRpZmZlcmVudCB5ZWFyLiBSUyBpbmRpY2VzIHdvdWxkIG5lZWQgdG8gYmUgY2FsY3VsYXRlZCBhZ2FpbiBmb3IgdGhvc2UuDQoNCiMjIEZsYWcgd2hlbiBjb29yZGluYXRlcyBhcmUgZGlmZmVyZW50IGJldHdlZW4gUlMgZGF0YSBhbmQgUmVTdXJ2ZXkgZGINCg0KYGBge3J9DQpkYl9yZXN1cnZfUlNfc2hvcnQgPC0gZGJfcmVzdXJ2X1JTX3Nob3J0ICU+JQ0KICBtdXRhdGUoTG9uX2RpZmYgPSBjYXNlX3doZW4oTG9uX3VwZGF0ZWQgPT0gTG9uX1JTIH4gIk5PIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgU29tZXRpbWVzIHRoZXkgYXJlIG9ubHkgc2xpZ2hseSBkaWZmZXJlbnQNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFicyhMb25fdXBkYXRlZCAtIExvbl9SUykgPCAwLjAxIH4gIlNNQUxMIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlzLm5hKExvbl91cGRhdGVkKSB8IGlzLm5hKExvbl9SUykgfiBOQSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRSVUUgfiAiTEFSR0UiKSwNCiAgICAgICAgIExhdF9kaWZmID0gY2FzZV93aGVuKExhdF91cGRhdGVkID09IExhdF9SUyB+ICJOTyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIFNvbWV0aW1lcyB0aGV5IGFyZSBvbmx5IHNsaWdobHkgZGlmZmVyZW50DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhYnMoTGF0X3VwZGF0ZWQgLSBMYXRfUlMpIDwgMC4wMSB+ICJTTUFMTCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpcy5uYShMYXRfdXBkYXRlZCkgfCBpcy5uYShMYXRfUlMpIH4gTkEsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUUlVFIH4gIkxBUkdFIikpDQpgYGANCg0KYGBge3J9DQpkYl9yZXN1cnZfUlNfc2hvcnQgJT4lIGNvdW50KExvbl9kaWZmKQ0KZGJfcmVzdXJ2X1JTX3Nob3J0ICU+JSBjb3VudChMYXRfZGlmZikNCmBgYA0KDQpWZXJ5IGZldyB3aXRoIGxhcmdlIGRpZmZlcmVuY2VzICgyIGZvciBsb25naXR1ZGUsIDYgZm9yIGxhdGl0dWRlKS4gUlMgaW5kaWNlcyB3b3VsZCBuZWVkIHRvIGJlIGNhbGN1bGF0ZWQgYWdhaW4gZm9yIHRob3NlLg0KDQpJZiB5ZWFyX2RpZmYgaXMgVFJVRSBvciBMb25fZGlmZiBpcyBMQVJHRSBvciBMYXRfZGlmZiBpcyBMQVJHRSAtLT4gUlMgaW5kaWNlcyBuZWVkIHRvIGJlIHJlY2FsY3VsYXRlZC4gU28gZmFyLCByZW1vdmUgdGhvc2Ugcm93cyBmcm9tIGRiX3Jlc3Vydl9SU19zaG9ydC4NCg0KYGBge3J9DQpkYl9yZXN1cnZfUlNfc2hvcnQgPC0gZGJfcmVzdXJ2X1JTX3Nob3J0ICU+JQ0KICBmaWx0ZXIoeWVhcl9kaWZmID09IEZBTFNFIHwgaXMubmEoeWVhcl9kaWZmKSkgJT4lDQogIGZpbHRlcihMb25fZGlmZiA9PSAiTk8iIHxMb25fZGlmZiA9PSAiU01BTEwiIHwgaXMubmEoTG9uX2RpZmYpKSAlPiUNCiAgZmlsdGVyKExhdF9kaWZmID09ICJOTyIgfExhdF9kaWZmID09ICJTTUFMTCIgfCBpcy5uYShMYXRfZGlmZikpDQpgYGANCg0KIyMgVE8tRE86IFJlY2FsY3VsYXRlIFJTIGluZGljZXMgZm9yIHRob3NlIGFib3ZlDQoNCiMjIEhhbmRsZSBwbG90cyB0aGF0IGhhdmUgbW9yZSB0aGFuIG9uZSBvYnMgcGVyIHllYXINCg0KQWRkIGNvbHVtbiBQTE9UIHRvIGRhdGEgdG8gaWRlbnRpZnkgdW5pcXVlIHBsb3RzOg0KDQpgYGB7cn0NCmRiX3Jlc3Vydl9SU19zaG9ydF9QTE9UIDwtIGRiX3Jlc3Vydl9SU19zaG9ydCAlPiUNCiAgIyBPcmlnaW5hbCBuYW1lcyBnaXZlIHByb2JsZW1zLCBjcmVhdGUgbmV3IHZhcnMNCiAgbXV0YXRlKFJTX3NpdGUgPSBgUmVTdXJ2ZXkgc2l0ZWAsIFJTX3Bsb3QgPSBgUmVTdXJ2ZXkgcGxvdGApICU+JQ0KICAjIENvbnZlcnQgdG8gZGF0YS50YWJsZSBmb3IgZmFzdGVyIHByb2Nlc3NpbmcNCiAgbGF6eV9kdCgpICU+JQ0KICAjIEdyb3VwIGJ5IHRoZSAzIHZhcnMgdGhhdCB1bmlxdWVseSBpZGVudGlmeSBlYWNoIHBsIG90DQogIGdyb3VwX2J5KFJTX0NPREUsIFJTX3NpdGUsIFJTX3Bsb3QpICU+JQ0KICAjIENyZWF0ZSBhIG5ldyB2YXJpYWJsZSBQTE9UIGZvciBlYWNoIGdyb3VwDQogIG11dGF0ZShQTE9UID0gLkdSUCkgJT4lDQogICMgQ29udmVydCBiYWNrIHRvIHRpYmJsZQ0KICBhc190aWJibGUoKSAlPiUNCiAgIyBSZW1vdmUgdW5uZWVkZWQgdmFycw0KICBzZWxlY3QoLVJTX3NpdGUsIC1SU19wbG90KQ0KYGBgDQoNClRoZXJlIHNob3VsZCBiZSBvbmx5IG9uZSBvYnNlcnZhdGlvbiBvZiBlYWNoIHBsb3QgcGVyIHllYXIuDQoNClBsb3RzIHdoZXJlIHRoZXJlIGlzIGF0IGxlYXN0IGEgeWVhciB3aXRoIG1vcmUgdGhhbiBvbmUgb2JzZXJ2YXRpb24sIGFuZCB3aGVyZSB0aG9zZSBvYnNlcnZhdGlvbnMgaGF2ZSBhIGRpZmZlcmVudCBFVU5JUyBhc3NpZ25lZDoNCg0KYGBge3J9DQpwbG90c190b19yZW1vdmUgPC0gZGJfcmVzdXJ2X1JTX3Nob3J0X1BMT1QgJT4lDQogIGdyb3VwX2J5KFBMT1QsIHllYXIpICU+JQ0KICBzdW1tYXJpemUoRVVOSVNhXzFfbiA9IG5fZGlzdGluY3QoRVVOSVNhXzEsIG5hLnJtID0gVFJVRSkpICU+JQ0KICB1bmdyb3VwKCkgJT4lIA0KICBmaWx0ZXIoRVVOSVNhXzFfbiA+IDEpICU+JQ0KICBkaXN0aW5jdChQTE9UKQ0KYGBgDQoNClJlbW92ZSBwbG90c190b19yZW1vdmUgZnJvbSB0aGUgZGF0YWJhc2U6DQoNCmBgYHtyfQ0KZGJfcmVzdXJ2X1JTX3Nob3J0X1BMT1QgPC0gZGJfcmVzdXJ2X1JTX3Nob3J0X1BMT1QgJT4lDQogIGFudGlfam9pbihwbG90c190b19yZW1vdmUsIGJ5ID0gIlBMT1QiKQ0KYGBgDQoNClBsb3RzIGFuZCB5ZWFycyB3aGVyZSB0aGVyZSBpcyBtb3JlIHRoYW4gb25lIG9ic2VydmF0aW9uOg0KDQpgYGB7cn0NCnBsb3RzX3RvX21lcmdlIDwtIGRiX3Jlc3Vydl9SU19zaG9ydF9QTE9UICU+JQ0KICBncm91cF9ieShQTE9ULCB5ZWFyKSAlPiUNCiAgIyBQbG90cyB0aGF0IGhhdmUgbW9yZSB0aGFuIG9uZSBvYnNlcnZhdGlvbiBwZXIgeWVhcg0KICBmaWx0ZXIobigpID4gMSkgJT4lDQogIHVuZ3JvdXAoKSAlPiUNCiAgZGlzdGluY3QoUExPVCkNCmBgYA0KDQpTdW1tYXJpemUgcGxvdHNfdG9fbWVyZ2U6DQoNCmBgYHtyfQ0KcGxvdHNfdG9fbWVyZ2Vfc3VtbSA8LSBkYl9yZXN1cnZfUlNfc2hvcnRfUExPVCAlPiUNCiAgZ3JvdXBfYnkoUExPVCwgeWVhcikgJT4lDQogICMgUGxvdHMgdGhhdCBoYXZlIG1vcmUgdGhhbiBvbmUgb2JzZXJ2YXRpb24gcGVyIHllYXINCiAgZmlsdGVyKG4oKSA+IDEpICU+JQ0KICBtdXRhdGUob2JzX251bSA9IHJvd19udW1iZXIoKSkgJT4lDQogIHBpdm90X3dpZGVyKA0KICAgIG5hbWVzX2Zyb20gPSBvYnNfbnVtLA0KICAgIHZhbHVlc19mcm9tID0gYyhkYXRlLCBQbG90T2JzZXJ2YXRpb25JRCksDQogICAgbmFtZXNfcHJlZml4ID0gIm9ic18iDQogICkgJT4lDQogIGFycmFuZ2UoUExPVCkgJT4lDQogIHN1bW1hcml6ZSgNCiAgICBhY3Jvc3MoYyhDb3VudHJ5LCBSU19DT0RFLCBgUmVTdXJ2ZXkgc2l0ZWAsIGBSZVN1cnZleSBwbG90YCwNCiAgICAgICAgICAgICBgTWFuaXB1bGF0ZSAoeS9uKWAsIGBUeXBlIG9mIG1hbmlwdWxhdGlvbmAsIExvbl91cGRhdGVkLA0KICAgICAgICAgICAgIExhdF91cGRhdGVkLCBgTG9jYXRpb24gbWV0aG9kYCwgYExvY2F0aW9uIHVuY2VydGFpbnR5IChtKWAsDQogICAgICAgICAgICAgRVVOSVNhXzEsIEVVTklTYV8xX2Rlc2NyLCBFVU5JU2FfMiwgRVVOSVNhXzJfZGVzY3IsIEVVTklTYV8zLA0KICAgICAgICAgICAgIEVVTklTYV8zX2Rlc2NyLCBFVU5JU2FfNCwgRVVOSVNhXzRfZGVzY3IsIEVVTklTMV9jb25mLA0KICAgICAgICAgICAgIEVVTklTMV9jb25mX3R5cGUsIGJpb2dlbywgdW5pdCwgeWVhcl9SUywgTG9uX1JTLCBMYXRfUlMsIE5EVklfbWF4LA0KICAgICAgICAgICAgIE5EVklfbWVkaWFuLCBORFZJX21pbiwgTkRWSV9tb2RlLCBORFZJX3AxMCwgTkRWSV9wOTAsIE5ETUlfbWF4LA0KICAgICAgICAgICAgIE5ETUlfbWVkaWFuLCBORE1JX21pbiwgTkRNSV9tb2RlLCBORE1JX3AxMCwgTkRNSV9wOTAsIE5EV0lfbWF4LA0KICAgICAgICAgICAgIE5EV0lfbWVkaWFuLCBORFdJX21pbiwgTkRXSV9tb2RlLCBORFdJX3AxMCwgTkRXSV9wOTAsIEVWSV9tYXgsDQogICAgICAgICAgICAgRVZJX21lZGlhbiwgRVZJX21pbiwgRVZJX21vZGUsIEVWSV9wMTAsIEVWSV9wOTAsIFNBVklfbWF4LA0KICAgICAgICAgICAgIFNBVklfbWVkaWFuLCBTQVZJX21pbiwgU0FWSV9tb2RlLCBTQVZJX3AxMCwgU0FWSV9wOTAsDQogICAgICAgICAgICAgY2Fub3B5X2hlaWdodCwgU09TX0RPWSwgU09TX2RhdGUsIFBlYWtfRE9ZLCBQZWFrX2RhdGUsIEVPU19ET1ksDQogICAgICAgICAgICAgRU9TX2RhdGUsIFMyX2RhdGEsIExhbmRzYXRfZGF0YSwgQ0hfZGF0YSwgUzJfcGhlbl9kYXRhLCB5ZWFyX2RpZmYsDQogICAgICAgICAgICAgTG9uX2RpZmYsIExhdF9kaWZmKSwgZmlyc3QpLA0KICAgIGFjcm9zcyhzdGFydHNfd2l0aCgiZGF0ZV9vYnNfIiksIG1pbiksDQogICAgYWNyb3NzKHN0YXJ0c193aXRoKCJQbG90T2JzZXJ2YXRpb25JRF9vYnNfIiksIG1pbikNCiAgICApICU+JQ0KICB1bmdyb3VwKCkNCmBgYA0KDQpSZW1vdmUgcGxvdHNfdG9fbWVyZ2UgZnJvbSB0aGUgZGF0YWJhc2U6DQoNCmBgYHtyfQ0KZGJfcmVzdXJ2X1JTX3Nob3J0X1BMT1QgPC0gZGJfcmVzdXJ2X1JTX3Nob3J0X1BMT1QgJT4lDQogIGFudGlfam9pbihwbG90c190b19tZXJnZSwgYnkgPSAiUExPVCIpDQpgYGANCg0KQW5kIGFkZCBwbG90c190b19tZXJnZV9zdW1tLCB3aGVyZSBlYWNoIHBsb3QgYW5kIHllYXIgb25seSBoYXMgb25lIHJvdzoNCg0KYGBge3J9DQpkYl9yZXN1cnZfUlNfc2hvcnRfUExPVCA8LSBiaW5kX3Jvd3MoZGJfcmVzdXJ2X1JTX3Nob3J0X1BMT1QsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGxvdHNfdG9fbWVyZ2Vfc3VtbSkNCmBgYA0KDQpDaGVjayB0aGF0IHRoZXJlIGlzIG9ubHkgb25lIHJvdyBwZXIgcGxvdCBhbmQgcGVyIHllYXI6DQoNCmBgYHtyfQ0KZGJfcmVzdXJ2X1JTX3Nob3J0X1BMT1QgJT4lDQogIGdyb3VwX2J5KFBMT1QsIHllYXIpICU+JQ0KICAjIFBsb3RzIHRoYXQgaGF2ZSBtb3JlIHRoYW4gb25lIG9ic2VydmF0aW9uIHBlciB5ZWFyDQogIGZpbHRlcihuKCkgPiAxKSANCmBgYA0KDQpTbywgdG8gc3VtIHVwIHdoYXQgSSBoYXZlIGRvbmU6DQoNCi0gUGxvdHMgd2hlcmUgdGhlcmUgaXMgYXQgbGVhc3QgYSB5ZWFyIHdpdGggbW9yZSB0aGFuIG9uZSBvYnNlcnZhdGlvbiwgYW5kIHdoZXJlIHRob3NlIG9ic2VydmF0aW9ucyBoYXZlIGEgZGlmZmVyZW50IEVVTklTIGFzc2lnbmVkOiBQbG90cyBSRU1PVkVEIGZyb20gdGhlIGRhdGENCi0gUGxvdHMgd2hlcmUgdGhlcmUgaXMgbW9yZSB0aGFuIG9uZSBvYnNlcnZhdGlvbiwgYnV0IG9ic2VydmF0aW9ucyBoYXZlIHRoZSBzYW1lIEVVTklTIGFzc2lnbmVkOiBrZXB0IGluIHRoZSBkYXRhLiBNZXJnZWQgc28gdGhhdCB0aGVyZSBpcyBvbmx5IG9uZSByb3cgcGVyIHllYXIuIEluZm8gYWJvdXQgdGhlIGRpZmZlcmVudCBkYXRlcyAod2hlbiBkaWZmZXJlbnQpIGlzIGtlcHQgaW4gY29sdW1ucyBkYXRlX29ic18xIC0gZGF0ZV9vYnNfNDAsIGFuZCBpbmZvIGFib3V0IHRoZSBkaWZmZXJlbnQgUGxvdE9ic2VydmF0aW9uSUQgaXMga2VwdCBpbiB0aGUgY29sdW1ucyBQbG90T2JzZXJ2YXRpb25JRF9vYnNfMSAtIFBsb3RPYnNlcnZhdGlvbklEX29ic180MC4NCg0KIyMjIFNhdmUgdG8gY2xlYW4gZGF0YQ0KDQpTYXZlIGNsZWFuIGZpbGUgZm9yIGFuYWx5c2VzICh0byBiZSB1cGRhdGVkIGNvbnRpbnVvdXNseSBkdWUgdG8gdXBkYXRlcyBpbiBSZVN1cnZleSBkYXRhYmFzZSBhbmQgdXBkYXRlcyBvbiBSUyBkYXRhKS4NCg0KYGBge3J9DQp3cml0ZV90c3YoZGJfcmVzdXJ2X1JTX3Nob3J0X1BMT1QsDQogICAgICAgICAgaGVyZSgiZGF0YSIsICJjbGVhbiIsImRiX3Jlc3Vydl9SU19zaG9ydF9QTE9UXzIwMjUwNTA1LmNzdiIpKQ0KYGBgDQoNCiMgRGlzdHJpYnV0aW9ucyBhbGwgYmlvcmVnaW9ucw0KDQpgYGB7cn0NCiMgRGVmaW5lIGEgZnVuY3Rpb24gdG8gY3JlYXRlIGhpc3RvZ3JhbXMNCnBsb3RfaGlzdG9ncmFtIDwtIGZ1bmN0aW9uKGRhdGEsIHhfdmFyLCB4X2xhYmVsKSB7DQogIGdncGxvdChkYXRhICU+JQ0KICAgICAgICAgICBmaWx0ZXIoRVVOSVNhXzEgJWluJSBjKCJUIiwgIlIiLCAiUyIsICJRIikpLA0KICAgICAgICAgYWVzKHggPSAhIXN5bSh4X3ZhcikpKSArDQogICAgZ2VvbV9oaXN0b2dyYW0oY29sb3IgPSAiYmxhY2siLCBmaWxsID0gIndoaXRlIikgKw0KICAgIGxhYnMoeCA9IHhfbGFiZWwsIHkgPSAiRnJlcXVlbmN5IikgKw0KICAgIHRoZW1lX2J3KCkNCn0NCmBgYA0KDQpgYGB7cn0NCiMgRGVmaW5lIGEgZnVuY3Rpb24gdG8gY3JlYXRlIHBsb3RzIHdpdGggdmlvbGluICsgYm94cGxvdCArIHBvaW50cw0KZGlzdHJfcGxvdCA8LSBmdW5jdGlvbihkYXRhLCB5X3ZhcnMsIHlfbGFiZWxzKSB7DQogIGZvciAoaSBpbiBzZXFfYWxvbmcoeV92YXJzKSkgew0KICAgIHlfdmFyIDwtIHlfdmFyc1tbaV1dDQogICAgeV9sYWJlbCA8LSB5X2xhYmVsc1tbaV1dDQogICAgDQogICAgcCA8LSBnZ3Bsb3QoZGF0YSA9IGRhdGEgJT4lDQogICAgICAgICAgICAgICAgICBmaWx0ZXIoRVVOSVNhXzEgJWluJSBjKCJUIiwgIlIiLCAiUyIsICJRIikpLA0KICAgICAgICAgICAgICAgIGFlcyh4ID0gRVVOSVNhXzFfZGVzY3IsIHkgPSAhIXN5bSh5X3ZhciksIGZpbGwgPSBFVU5JU2FfMV9kZXNjcikpICsNCiAgICAgIGdlb21fZmxhdF92aW9saW4ocG9zaXRpb24gPSBwb3NpdGlvbl9udWRnZSh4ID0gMC4yLCB5ID0gMCksIGFscGhhID0gMC44KSArDQogICAgICBnZW9tX3BvaW50KGFlcyh5ID0gISFzeW0oeV92YXIpLCBjb2xvciA9IEVVTklTYV8xX2Rlc2NyKSwNCiAgICAgICAgICAgICAgICAgcG9zaXRpb24gPSBwb3NpdGlvbl9qaXR0ZXIod2lkdGggPSAwLjE1KSwgc2l6ZSA9IDEsIGFscGhhID0gMC4yNSkgKw0KICAgICAgZ2VvbV9ib3hwbG90KHdpZHRoID0gMC4yLCBvdXRsaWVyLnNoYXBlID0gTkEsIGFscGhhID0gMC41KSArDQogICAgICBzdGF0X3N1bW1hcnkoZnVuLnkgPSBtZWFuLCBnZW9tID0gInBvaW50Iiwgc2hhcGUgPSAyMCwgc2l6ZSA9IDEpICsNCiAgICAgIHN0YXRfc3VtbWFyeShmdW4uZGF0YSA9IGZ1bmN0aW9uKHgpIGRhdGEuZnJhbWUoeSA9IG1heCh4KSArIDAuMSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWwgPSBsZW5ndGgoeCkpLA0KICAgICAgICAgICAgICAgICAgIGdlb20gPSAidGV4dCIsIGFlcyhsYWJlbCA9IC4ubGFiZWwuLiksIHZqdXN0ID0gMC41KSArDQogICAgICBsYWJzKHkgPSB5X2xhYmVsLCB4ID0gIkVVTklTIGxldmVsIDEiKSArDQogICAgICBzY2FsZV94X2Rpc2NyZXRlKGxhYmVscyA9IGZ1bmN0aW9uKHgpIHN0cl93cmFwKHgsIHdpZHRoID0gMTUpKSArDQogICAgICBndWlkZXMoZmlsbCA9IEZBTFNFLCBjb2xvciA9IEZBTFNFKSArDQogICAgICB0aGVtZV9idygpICsgY29vcmRfZmxpcCgpDQogICAgDQogICAgcHJpbnQocCkNCiAgfQ0KfQ0KYGBgDQoNCiMjIEhFUkU6IE1ldHJpY3MgY2FsY3VsYXRlZCBvbmx5IGZvciBKdWx5LCBub3QgZm9yIGFsbCB5ZWFyIQ0KDQojIyBORFZJLCBORE1JLCBORFdJLCBTQVZJIGFuZCBFVkkNCg0KSGlzdG9ncmFtcyB0byBjaGVjayB0aGF0IHZhbHVlcyBhcmUgb2s6DQoNCmBgYHtyfQ0KaGlzdF9ORFZJIDwtIHBsb3RfaGlzdG9ncmFtKGRiX3Jlc3Vydl9SU19zaG9ydF9QTE9ULCAiTkRWSV9tYXgiLCAiTkRWSSBtYXgiKQ0KaGlzdF9ORE1JIDwtIHBsb3RfaGlzdG9ncmFtKGRiX3Jlc3Vydl9SU19zaG9ydF9QTE9ULCAiTkRNSV9tYXgiLCAiTkRNSSBtYXgiKQ0KaGlzdF9ORFdJIDwtIHBsb3RfaGlzdG9ncmFtKGRiX3Jlc3Vydl9SU19zaG9ydF9QTE9ULCAiTkRXSV9tYXgiLCAiTkRXSSBtYXgiKQ0KaGlzdF9TQVZJIDwtIHBsb3RfaGlzdG9ncmFtKGRiX3Jlc3Vydl9SU19zaG9ydF9QTE9ULCAiU0FWSV9tYXgiLCAiU0FWSSBtYXgiKQ0KaGlzdF9FVkkgPC0gcGxvdF9oaXN0b2dyYW0oZGJfcmVzdXJ2X1JTX3Nob3J0X1BMT1QsICJFVklfbWF4IiwgIkVWSSBtYXgiKQ0KaGlzdF9ORFZJDQpoaXN0X05ETUkNCmhpc3RfTkRXSQ0KaGlzdF9TQVZJDQpoaXN0X0VWSSAjIFNvbWUgdmFsdWVzIHdyb25nIQ0KaGlzdF9FVklfMSA8LSBwbG90X2hpc3RvZ3JhbShkYl9yZXN1cnZfUlNfc2hvcnRfUExPVCAlPiUgZmlsdGVyKEVWSV9tYXggPD0gMSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICJFVklfbWF4IiwgIkVWSSBtYXgiKQ0KaGlzdF9FVklfMQ0KYGBgDQoNCmBgYHtyfQ0KbnJvdyhkYl9yZXN1cnZfUlNfc2hvcnRfUExPVCAlPiUNCiAgICAgICBmaWx0ZXIoRVVOSVNhXzEgJWluJSBjKCJUIiwgIlIiLCAiUyIsICJRIikpICU+JQ0KICAgICAgIGZpbHRlcihFVklfbWF4ID4gMSkpDQpkYl9yZXN1cnZfUlNfc2hvcnRfUExPVCAlPiUNCiAgICAgICBmaWx0ZXIoRVVOSVNhXzEgJWluJSBjKCJUIiwgIlIiLCAiUyIsICJRIikpJT4lDQogIGZpbHRlcihFVklfbWF4ID4gMSkgJT4lDQogIGNvdW50KGJpb2dlbywgdW5pdCkNCmBgYA0KDQpTbyBmYXIsIHJlbW92ZSBvYnNlcnZhdGlvbnMgd2l0aCBFVklfbWF4ID4gMSBmcm9tIHN0dWZmIHVzaW5nIEVWSSB2YWx1ZXMuIA0KDQpEaXN0cmlidXRpb24gcGxvdHM6DQoNCmBgYHtyfQ0KZGlzdHJfcGxvdChkYl9yZXN1cnZfUlNfc2hvcnRfUExPVCwNCiAgICAgICAgICAgYygiTkRWSV9tYXgiLCAiTkRWSV9wOTAiLCAiTkRWSV9taW4iLCAiTkRWSV9wMTAiKSwgDQogICAgICAgICAgIGMoIk5EVkkgbWF4IiwgIk5EVkkgcDkwIiwgIk5EVkkgbWluIiwgIk5EVkkgcDEwIikpDQpkaXN0cl9wbG90KGRiX3Jlc3Vydl9SU19zaG9ydF9QTE9ULA0KICAgICAgICAgICBjKCJORE1JX21heCIsICJORE1JX3A5MCIsICJORE1JX21pbiIsICJORE1JX3AxMCIpLCANCiAgICAgICAgICAgYygiTkRNSSBtYXgiLCAiTkRNSSBwOTAiLCAiTkRNSSBtaW4iLCAiTkRNSSBwMTAiKSkNCmRpc3RyX3Bsb3QoZGJfcmVzdXJ2X1JTX3Nob3J0X1BMT1QsDQogICAgICAgICAgIGMoIk5EV0lfbWF4IiwgIk5EV0lfcDkwIiwgIk5EV0lfbWluIiwgIk5EV0lfcDEwIiksIA0KICAgICAgICAgICBjKCJORFdJIG1heCIsICJORFdJIHA5MCIsICJORFdJIG1pbiIsICJORFdJIHAxMCIpKQ0KZGlzdHJfcGxvdChkYl9yZXN1cnZfUlNfc2hvcnRfUExPVCwNCiAgICAgICAgICAgYygiU0FWSV9tYXgiLCAiU0FWSV9wOTAiLCAiU0FWSV9taW4iLCAiU0FWSV9wMTAiKSwgDQogICAgICAgICAgIGMoIlNBVkkgbWF4IiwgIlNBVkkgcDkwIiwgIlNBVkkgbWluIiwgIlNBVkkgcDEwIikpDQpkaXN0cl9wbG90KGRiX3Jlc3Vydl9SU19zaG9ydF9QTE9UICU+JSBmaWx0ZXIoRVZJX21heCA8PSAxKSwNCiAgICAgICAgICAgYygiRVZJX21heCIsICJFVklfcDkwIiwgIkVWSV9taW4iLCAiRVZJX3AxMCIpLCANCiAgICAgICAgICAgYygiRVZJIG1heCIsICJFVkkgcDkwIiwgIkVWSSBtaW4iLCAiRVZJIHAxMCIpKQ0KYGBgDQoNCiMjIENIDQoNCmBgYHtyfQ0KcGxvdF9kaXN0cl9DSCA8LSBkaXN0cl9wbG90KGRiX3Jlc3Vydl9SU19zaG9ydF9QTE9ULCAiY2Fub3B5X2hlaWdodCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIkNhbm9weSBoZWlnaHQgKG0pIikNCnBsb3RfZGlzdHJfQ0gNCmBgYA0KIA0KIyMjIFNob3cgaGFiaXRhdHMgd2l0aCBDSCBjYXRlZ29yaWVzDQoNCmBgYHtyfQ0KZ2dwbG90KGRiX3Jlc3Vydl9SU19zaG9ydF9QTE9UICU+JQ0KICAgICAgICAgIyBLZWVwIG9ubHkgZm9yZXN0cywgZ3Jhc3NsYW5kcywgc2hydWJsYW5kcyBhbmQgd2V0bGFuZHMNCiAgICAgICAgIGZpbHRlcihFVU5JU2FfMSAlaW4lIGMoIlQiLCAiUiIsICJTIiwgIlEiKSkgJT4lDQogICAgICAgICBtdXRhdGUoQ0hfY2F0ID0NCiAgICAgICAgICAgICAgICAgIGZhY3RvcigNCiAgICAgICAgICAgICAgICAgICAgY2FzZV93aGVuKGNhbm9weV9oZWlnaHQgPT0gMCB+ICIwIG0iLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2Fub3B5X2hlaWdodCA+IDAgJiBjYW5vcHlfaGVpZ2h0IDw9IDEgfiAiMC0xIG0iLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2Fub3B5X2hlaWdodCA+IDEgJiBjYW5vcHlfaGVpZ2h0IDw9MiB+ICIxLTIgbSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYW5vcHlfaGVpZ2h0ID4gMiAmIGNhbm9weV9oZWlnaHQgPD01IH4gIjItNSBtIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhbm9weV9oZWlnaHQgPiA1ICYgY2Fub3B5X2hlaWdodCA8PTggfiAiNS04IG0iLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2Fub3B5X2hlaWdodCA+IDggfiAiPiA4IG0iLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaXMubmEoY2Fub3B5X2hlaWdodCkgfiBOQV9jaGFyYWN0ZXJfKSwNCiAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gYygNCiAgICAgICAgICAgICAgICAgICAgICAiMCBtIiwgIjAtMSBtIiwgIjEtMiBtIiwgIjItNSBtIiwgIjUtOCBtIiwgIj4gOCBtIikpKSwNCiAgICAgICBhZXMoeCA9IEVVTklTYV8xX2Rlc2NyLCBmaWxsID0gQ0hfY2F0KSkgKw0KICBnZW9tX2JhcigpICsgdGhlbWVfYncoKSArIGNvb3JkX2ZsaXAoKSArDQogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBsYWJlbF9udW1iZXIoKSkgKw0KICBzY2FsZV9maWxsX3ZpcmlkaXNfZChkaXJlY3Rpb24gPSAtMSkgKw0KICBsYWJzKHggPSAiRVVOSVMgbGV2ZWwgMSIsIGZpbGwgPSAiQ2Fub3B5IGhlaWdodCIpICsNCiAgc2NhbGVfeF9kaXNjcmV0ZShsYWJlbHMgPSBmdW5jdGlvbih4KSBzdHJfd3JhcCh4LCB3aWR0aCA9IDE1KSkgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSBjKDAuOCwgMC43NSksDQogICAgICAgIGxlZ2VuZC5kaXJlY3Rpb24gPSAidmVydGljYWwiKQ0KYGBgDQoNCiMjIyBTdGF0cyBwZXIgaGFiaXRhdCB0eXBlDQoNCmBgYHtyfQ0KZGJfcmVzdXJ2X1JTX3Nob3J0X1BMT1QgJT4lDQogICMgS2VlcCBvbmx5IGZvcmVzdHMsIGdyYXNzbGFuZHMsIHNocnVibGFuZHMgYW5kIHdldGxhbmRzDQogIGZpbHRlcihFVU5JU2FfMSAlaW4lIGMoIlQiLCAiUiIsICJTIiwgIlEiKSkgJT4lDQogIGdyb3VwX2J5KEVVTklTYV8xX2Rlc2NyKSAlPiUNCiAgc3VtbWFyaXNlKGFjcm9zcyhjYW5vcHlfaGVpZ2h0LCBsaXN0KA0KICAgIG1lYW4gPSBtZWFuLA0KICAgIG1lZGlhbiA9IG1lZGlhbiwNCiAgICBzZCA9IHNkLA0KICAgIG1pbiA9IG1pbiwNCiAgICBtYXggPSBtYXgNCiAgICApLCBuYS5ybSA9IFRSVUUpKQ0KYGBgDQoNCiMjIFBoZW5vbG9neQ0KDQojIyMgQ2FsY3VsYXRlIG1ldHJpY3MNCg0KYGBge3J9DQpkYl9yZXN1cnZfUlNfc2hvcnRfUExPVCA8LSBkYl9yZXN1cnZfUlNfc2hvcnRfUExPVCAlPiUNCiAgbXV0YXRlKA0KICAgICMgRGlmZmVyZW5jZSBORFZJIGJldHdlZW4gUGVhayBhbmQgU09TDQogICAgZGlmZl9QZWFrX1NPUyA9IE5EVklfYXRfUGVhayAtIE5EVklfYXRfU09TLA0KICAgICMgRGlmZmVyZW5jZSBORFZJIGJldHdlZW4gUGVhayBhbmQgRU9TDQogICAgZGlmZl9QZWFrX0VPUyA9IE5EVklfYXRfUGVhayAtIE5EVklfYXRfRU9TKQ0KYGBgDQoNCiMjIyBIaXN0b2dyYW1zIHBoZW5vbG9neSBtZWFzdXJlcw0KDQpgYGB7cn0NCmdncGxvdChkYXRhID0gZGJfcmVzdXJ2X1JTX3Nob3J0X1BMT1QgJT4lDQogICAgICAgICAjIEtlZXAgb25seSBmb3Jlc3RzLCBncmFzc2xhbmRzLCBzaHJ1YmxhbmRzIGFuZCB3ZXRsYW5kcw0KICAgICAgICAgZmlsdGVyKEVVTklTYV8xICVpbiUgYygiVCIsICJSIiwgIlMiLCAiUSIpICYgUzJfcGhlbl9kYXRhID09IFQpICU+JQ0KICAgICAgICAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBjKFNPU19ET1ksIFBlYWtfRE9ZLCBFT1NfRE9ZKSwgbmFtZXNfdG8gPSAibmFtZSIsDQogICAgICAgICAgICAgICAgICAgICAgdmFsdWVzX3RvID0gInZhbHVlIiksDQogICAgICAgYWVzKHggPSB2YWx1ZSkpICsNCiAgZ2VvbV9oaXN0b2dyYW0oZmlsbCA9ICJ3aGl0ZSIsIGNvbG9yID0gImJsYWNrIikgKw0KICBmYWNldF9ncmlkKGJpb2dlbyB+IG5hbWUsIHNjYWxlcyA9ICJmcmVlX3kiKSArDQogIHRoZW1lX2J3KCkNCmdncGxvdChkYXRhID0gZGJfcmVzdXJ2X1JTX3Nob3J0X1BMT1QgJT4lDQogICAgICAgICAjIEtlZXAgb25seSBmb3Jlc3RzLCBncmFzc2xhbmRzLCBzaHJ1YmxhbmRzIGFuZCB3ZXRsYW5kcw0KICAgICAgICAgZmlsdGVyKEVVTklTYV8xICVpbiUgYygiVCIsICJSIiwgIlMiLCAiUSIpICYgUzJfcGhlbl9kYXRhID09IFQpICU+JQ0KICAgICAgICAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBjKE5EVklfYXRfU09TLCBORFZJX2F0X1BlYWssIE5EVklfYXRfRU9TKSwNCiAgICAgICAgICAgICAgICAgICAgICBuYW1lc190byA9ICJuYW1lIiwgdmFsdWVzX3RvID0gInZhbHVlIiksDQogICAgICAgYWVzKHggPSB2YWx1ZSkpICsNCiAgZ2VvbV9oaXN0b2dyYW0oZmlsbCA9ICJ3aGl0ZSIsIGNvbG9yID0gImJsYWNrIikgKw0KICBmYWNldF9ncmlkKGJpb2dlbyB+IG5hbWUsIHNjYWxlcyA9ICJmcmVlX3kiKSArDQogIHRoZW1lX2J3KCkNCmdncGxvdChkYXRhID0gZGJfcmVzdXJ2X1JTX3Nob3J0X1BMT1QgJT4lDQogICAgICAgICAjIEtlZXAgb25seSBmb3Jlc3RzLCBncmFzc2xhbmRzLCBzaHJ1YmxhbmRzIGFuZCB3ZXRsYW5kcw0KICAgICAgICAgZmlsdGVyKEVVTklTYV8xICVpbiUgYygiVCIsICJSIiwgIlMiLCAiUSIpICYgUzJfcGhlbl9kYXRhID09IFQpICU+JQ0KICAgICAgICAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBjKGRpZmZfUGVha19TT1MsIGRpZmZfUGVha19FT1MpLA0KICAgICAgICAgICAgICAgICAgICAgIG5hbWVzX3RvID0gIm5hbWUiLCB2YWx1ZXNfdG8gPSAidmFsdWUiKSwNCiAgICAgICBhZXMoeCA9IHZhbHVlKSkgKw0KICBnZW9tX2hpc3RvZ3JhbShmaWxsID0gIndoaXRlIiwgY29sb3IgPSAiYmxhY2siKSArDQogIGZhY2V0X2dyaWQoYmlvZ2VvIH4gbmFtZSwgc2NhbGVzID0gImZyZWVfeSIpICsNCiAgdGhlbWVfYncoKQ0KZ2dwbG90KGRhdGEgPSBkYl9yZXN1cnZfUlNfc2hvcnRfUExPVCAlPiUNCiAgICAgICAgICMgS2VlcCBvbmx5IGZvcmVzdHMsIGdyYXNzbGFuZHMsIHNocnVibGFuZHMgYW5kIHdldGxhbmRzDQogICAgICAgICBmaWx0ZXIoRVVOSVNhXzEgJWluJSBjKCJUIiwgIlIiLCAiUyIsICJRIikgJiBTMl9waGVuX2RhdGEgPT0gVCkgJT4lDQogICAgICAgICBwaXZvdF9sb25nZXIoY29scyA9IGMoU2Vhc29uX0xlbmd0aCksDQogICAgICAgICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAibmFtZSIsIHZhbHVlc190byA9ICJ2YWx1ZSIpLA0KICAgICAgIGFlcyh4ID0gdmFsdWUpKSArDQogIGdlb21faGlzdG9ncmFtKGZpbGwgPSAid2hpdGUiLCBjb2xvciA9ICJibGFjayIpICsNCiAgZmFjZXRfZ3JpZChiaW9nZW8gfiBuYW1lLCBzY2FsZXMgPSAiZnJlZV95IikgKw0KICB0aGVtZV9idygpDQpgYGANCg0KIyMjIERpc3RyaWJ1dGlvbnMNCg0KYGBge3J9DQpwbG90X2Rpc3RyX1NPU19ET1kgPC0gZGlzdHJfcGxvdChkYl9yZXN1cnZfUlNfc2hvcnRfUExPVCwgIlNPU19ET1kiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlNPUyBET1kiKQ0KcGxvdF9kaXN0cl9QZWFrX0RPWSA8LSBkaXN0cl9wbG90KGRiX3Jlc3Vydl9SU19zaG9ydF9QTE9ULCAiUGVha19ET1kiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJQZWFrIERPWSIpDQpwbG90X2Rpc3RyX0VPU19ET1kgPC0gZGlzdHJfcGxvdChkYl9yZXN1cnZfUlNfc2hvcnRfUExPVCwgIkVPU19ET1kiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkVPUyBET1kiKQ0KcGxvdF9kaXN0cl9ORFZJX2F0X1NPUyA8LSBkaXN0cl9wbG90KGRiX3Jlc3Vydl9SU19zaG9ydF9QTE9ULCAiTkRWSV9hdF9TT1MiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJORFZJIGF0IFNPUyIpDQpwbG90X2Rpc3RyX05EVklfYXRfUGVhayA8LSBkaXN0cl9wbG90KGRiX3Jlc3Vydl9SU19zaG9ydF9QTE9ULCAiTkRWSV9hdF9QZWFrIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk5EVkkgYXQgUGVhayIpDQpwbG90X2Rpc3RyX05EVklfYXRfRU9TIDwtIGRpc3RyX3Bsb3QoZGJfcmVzdXJ2X1JTX3Nob3J0X1BMT1QsICJORFZJX2F0X0VPUyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk5EVkkgYXQgRU9TIikNCnBsb3RfZGlzdHJfZGlmZl9QZWFrX1NPUyA8LSBkaXN0cl9wbG90KGRiX3Jlc3Vydl9SU19zaG9ydF9QTE9ULCAiZGlmZl9QZWFrX1NPUyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRGlmZmVyZW5jZSBQZWFrLVNPUyIpDQpwbG90X2Rpc3RyX2RpZmZfUGVha19FT1MgPC0gZGlzdHJfcGxvdChkYl9yZXN1cnZfUlNfc2hvcnRfUExPVCwgImRpZmZfUGVha19FT1MiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkRpZmZlcmVuY2UgUGVhay1FT1MiKQ0KcGxvdF9kaXN0cl9TZWFzb25fTGVuZ3RoIDwtIGRpc3RyX3Bsb3QoZGJfcmVzdXJ2X1JTX3Nob3J0X1BMT1QsICJTZWFzb25fTGVuZ3RoIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJTZWFzb24gTGVuZ3RoIikNCnBsb3RfZGlzdHJfU09TX0RPWQ0KcGxvdF9kaXN0cl9QZWFrX0RPWQ0KcGxvdF9kaXN0cl9FT1NfRE9ZDQpwbG90X2Rpc3RyX05EVklfYXRfU09TDQpwbG90X2Rpc3RyX05EVklfYXRfUGVhaw0KcGxvdF9kaXN0cl9ORFZJX2F0X0VPUw0KcGxvdF9kaXN0cl9kaWZmX1BlYWtfU09TDQpwbG90X2Rpc3RyX2RpZmZfUGVha19FT1MNCnBsb3RfZGlzdHJfU2Vhc29uX0xlbmd0aA0KYGBgDQoNCiMgRGlzdHJpYnV0aW9ucyBwZXIgYmlvcmVnaW9uDQoNCmBgYHtyfQ0KIyBEZWZpbmUgYSBmdW5jdGlvbiB0byBjcmVhdGUgcGxvdHMgd2l0aCB2aW9saW4gKyBib3hwbG90ICsgcG9pbnRzDQpkaXN0cl9wbG90X2Jpb2dlbyA8LSBmdW5jdGlvbihkYXRhLCB5X3ZhciwgeV9sYWJlbCkgew0KICBnZ3Bsb3QoZGF0YSA9IGRhdGEgJT4lDQogICAgICAgICAgIGZpbHRlcihFVU5JU2FfMSAlaW4lIGMoIlQiLCAiUiIsICJTIiwgIlEiKSksDQogICAgICAgICBhZXMoeCA9IEVVTklTYV8xX2Rlc2NyLCB5ID0gISFzeW0oeV92YXIpLCBmaWxsID0gRVVOSVNhXzFfZGVzY3IpKSArDQogICAgZ2VvbV9mbGF0X3Zpb2xpbihwb3NpdGlvbiA9IHBvc2l0aW9uX251ZGdlKHggPSAwLjIsIHkgPSAwKSwgYWxwaGEgPSAwLjgpICsNCiAgICBnZW9tX3BvaW50KGFlcyh5ID0gISFzeW0oeV92YXIpLCBjb2xvciA9IEVVTklTYV8xX2Rlc2NyKSwNCiAgICAgICAgICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25faml0dGVyKHdpZHRoID0gMC4xNSksIHNpemUgPSAxLCBhbHBoYSA9IDAuMjUpICsNCiAgICBnZW9tX2JveHBsb3Qod2lkdGggPSAwLjIsIG91dGxpZXIuc2hhcGUgPSBOQSwgYWxwaGEgPSAwLjUpICsNCiAgICBzdGF0X3N1bW1hcnkoZnVuLnkgPSBtZWFuLCBnZW9tID0gInBvaW50Iiwgc2hhcGUgPSAyMCwgc2l6ZSA9IDEpICsNCiAgICBzdGF0X3N1bW1hcnkoZnVuLmRhdGEgPSBmdW5jdGlvbih4KSBkYXRhLmZyYW1lKHkgPSBtYXgoeCkgKyAwLjEsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbCA9IGxlbmd0aCh4KSksDQogICAgICAgICAgICAgICAgIGdlb20gPSAidGV4dCIsIGFlcyhsYWJlbCA9IC4ubGFiZWwuLiksIHZqdXN0ID0gMC41KSArDQogICAgbGFicyh5ID0geV9sYWJlbCwgeCA9ICJFVU5JU2FfMV9kZXNjciIpICsNCiAgICBzY2FsZV94X2Rpc2NyZXRlKGxhYmVscyA9IGZ1bmN0aW9uKHgpIHN0cl93cmFwKHgsIHdpZHRoID0gMTUpKSArDQogICAgZ3VpZGVzKGZpbGwgPSBGQUxTRSwgY29sb3IgPSBGQUxTRSkgKw0KICAgIHRoZW1lX2J3KCkgKyBjb29yZF9mbGlwKCkgKyBmYWNldF93cmFwKH4gYmlvZ2VvKQ0KfQ0KYGBgDQoNCiMjIE1heC4gTkRWSSwgTkRNSSwgTkRXSSwgU0FWSSBhbmQgRVZJDQoNCkRpc3RyaWJ1dGlvbiBwbG90czoNCg0KYGBge3J9DQpwbG90X2Rpc3RyX05EVklfYmlvZ2VvIDwtIGRpc3RyX3Bsb3RfYmlvZ2VvKGRiX3Jlc3Vydl9SU19zaG9ydF9QTE9UICU+JQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbHRlcighaXMubmEoYmlvZ2VvKSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJORFZJX21heCIsICJORFZJIG1heCIpDQpwbG90X2Rpc3RyX05ETUlfYmlvZ2VvIDwtIGRpc3RyX3Bsb3RfYmlvZ2VvKGRiX3Jlc3Vydl9SU19zaG9ydF9QTE9UICU+JQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbHRlcighaXMubmEoYmlvZ2VvKSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJORE1JX21heCIsICJORE1JIG1heCIpDQpwbG90X2Rpc3RyX05EV0lfYmlvZ2VvIDwtIGRpc3RyX3Bsb3RfYmlvZ2VvKGRiX3Jlc3Vydl9SU19zaG9ydF9QTE9UICU+JQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbHRlcighaXMubmEoYmlvZ2VvKSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJORFdJX21heCIsICJORFdJIG1heCIpDQpwbG90X2Rpc3RyX1NBVklfYmlvZ2VvIDwtIGRpc3RyX3Bsb3RfYmlvZ2VvKGRiX3Jlc3Vydl9SU19zaG9ydF9QTE9UICU+JQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbHRlcighaXMubmEoYmlvZ2VvKSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJTQVZJX21heCIsICJTQVZJIG1heCIpDQpwbG90X2Rpc3RyX0VWSV9iaW9nZW8gPC0gZGlzdHJfcGxvdF9iaW9nZW8oZGJfcmVzdXJ2X1JTX3Nob3J0X1BMT1QgJT4lDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsdGVyKCFpcy5uYShiaW9nZW8pKSAlPiUNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbHRlcihFVklfbWF4IDw9IDEpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJFVklfbWF4IiwgIkVWSSBtYXgiKQ0KcGxvdF9kaXN0cl9ORFZJX2Jpb2dlbw0KcGxvdF9kaXN0cl9ORE1JX2Jpb2dlbw0KcGxvdF9kaXN0cl9ORFdJX2Jpb2dlbw0KcGxvdF9kaXN0cl9TQVZJX2Jpb2dlbw0KcGxvdF9kaXN0cl9FVklfYmlvZ2VvDQpgYGANCg0KQk9SIG1pc3NpbmcgYmVjYXVzZSB0aGVyZSBpcyBubyBFVU5JUyBpbmZvIGZvciB0aG9zZSB0aGF0IGhhdmUgUlMgaW5mby4NCg0KIyMgQ0gNCg0KYGBge3J9DQpwbG90X2Rpc3RyX0NIX2Jpb2dlbyA8LSBkaXN0cl9wbG90X2Jpb2dlbyhkYl9yZXN1cnZfUlNfc2hvcnRfUExPVCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJjYW5vcHlfaGVpZ2h0IiwgIkNhbm9weSBoZWlnaHQgKG0pIikNCnBsb3RfZGlzdHJfQ0hfYmlvZ2VvDQpgYGANCg0KSW4gdGhpcyBwbG90LCB0aG9zZSB3aXRoIGJpb2dlbyA9IE5BIGFyZSB0aG9zZSB0aGF0IGRvIG5vdCBoYXZlIFMyIG9yIExhbmRzYXQgZGF0YSAoYW5kIHRodXMgYmlvZ2VvIGhhcyBub3QgYmVlbiBhc3NpZ25lZCksIGJ1dCBoYXZlIENIIGRhdGEuIFdlIHNob3VsZCBsYXRlciBhc3NpZ24gYSBiaW9nZW8gYmFzZWQgb24gbG9jYXRpb24uIA0KDQojIyBQaGVub2xvZ3kNCg0KYGBge3J9DQpwbG90X2Rpc3RyX1NPU19ET1lfYmlvZ2VvIDwtIGRpc3RyX3Bsb3RfYmlvZ2VvKGRiX3Jlc3Vydl9SU19zaG9ydF9QTE9UICU+JQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbHRlcighaXMubmEoYmlvZ2VvKSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJTT1NfRE9ZIiwgIlNPUyBET1kiKQ0KcGxvdF9kaXN0cl9QZWFrX0RPWV9iaW9nZW8gPC0gZGlzdHJfcGxvdF9iaW9nZW8oZGJfcmVzdXJ2X1JTX3Nob3J0X1BMT1QgJT4lDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsdGVyKCFpcy5uYShiaW9nZW8pKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJQZWFrX0RPWSIsICJQZWFrIERPWSIpDQpwbG90X2Rpc3RyX0VPU19ET1lfYmlvZ2VvIDwtIGRpc3RyX3Bsb3RfYmlvZ2VvKGRiX3Jlc3Vydl9SU19zaG9ydF9QTE9UICU+JQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbHRlcighaXMubmEoYmlvZ2VvKSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJFT1NfRE9ZIiwgIkVPUyBET1kiKQ0KcGxvdF9kaXN0cl9ORFZJX2F0X1NPU19iaW9nZW8gPC0gZGlzdHJfcGxvdF9iaW9nZW8oZGJfcmVzdXJ2X1JTX3Nob3J0X1BMT1QgJT4lDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsdGVyKCFpcy5uYShiaW9nZW8pKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk5EVklfYXRfU09TIiwgIk5EVkkgYXQgU09TIikNCnBsb3RfZGlzdHJfTkRWSV9hdF9QZWFrX2Jpb2dlbyA8LSBkaXN0cl9wbG90X2Jpb2dlbyhkYl9yZXN1cnZfUlNfc2hvcnRfUExPVCAlPiUNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWx0ZXIoIWlzLm5hKGJpb2dlbykpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk5EVklfYXRfUGVhayIsICJORFZJIGF0IFBlYWsiKQ0KcGxvdF9kaXN0cl9ORFZJX2F0X0VPU19iaW9nZW8gPC0gZGlzdHJfcGxvdF9iaW9nZW8oZGJfcmVzdXJ2X1JTX3Nob3J0X1BMT1QgJT4lDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsdGVyKCFpcy5uYShiaW9nZW8pKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk5EVklfYXRfRU9TIiwgIk5EVkkgYXQgRU9TIikNCnBsb3RfZGlzdHJfZGlmZl9QZWFrX1NPU19iaW9nZW8gPC0gZGlzdHJfcGxvdF9iaW9nZW8oZGJfcmVzdXJ2X1JTX3Nob3J0X1BMT1QgJT4lDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsdGVyKCFpcy5uYShiaW9nZW8pKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImRpZmZfUGVha19TT1MiLCAiRGlmZmVyZW5jZSBQZWFrLVNPUyIpDQpwbG90X2Rpc3RyX2RpZmZfUGVha19FT1NfYmlvZ2VvIDwtIGRpc3RyX3Bsb3RfYmlvZ2VvKGRiX3Jlc3Vydl9SU19zaG9ydF9QTE9UICU+JQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbHRlcighaXMubmEoYmlvZ2VvKSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJkaWZmX1BlYWtfRU9TIiwgIkRpZmZlcmVuY2UgUGVhay1FT1MiKQ0KcGxvdF9kaXN0cl9TZWFzb25fTGVuZ3RoX2Jpb2dlbyA8LSBkaXN0cl9wbG90X2Jpb2dlbyhkYl9yZXN1cnZfUlNfc2hvcnRfUExPVCAlPiUNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWx0ZXIoIWlzLm5hKGJpb2dlbykpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiU2Vhc29uX0xlbmd0aCIsICJTZWFzb24gTGVuZ3RoIikNCg0KcGxvdF9kaXN0cl9TT1NfRE9ZX2Jpb2dlbw0KcGxvdF9kaXN0cl9QZWFrX0RPWV9iaW9nZW8NCnBsb3RfZGlzdHJfRU9TX0RPWV9iaW9nZW8NCnBsb3RfZGlzdHJfTkRWSV9hdF9TT1NfYmlvZ2VvDQpwbG90X2Rpc3RyX05EVklfYXRfUGVha19iaW9nZW8NCnBsb3RfZGlzdHJfTkRWSV9hdF9FT1NfYmlvZ2VvDQpwbG90X2Rpc3RyX2RpZmZfUGVha19TT1NfYmlvZ2VvDQpwbG90X2Rpc3RyX2RpZmZfUGVha19FT1NfYmlvZ2VvDQpwbG90X2Rpc3RyX1NlYXNvbl9MZW5ndGhfYmlvZ2VvDQpgYGANCg0KQk9SIG1pc3NpbmcgYmVjYXVzZSB0aGVyZSBpcyBubyBFVU5JUyBpbmZvIGZvciB0aG9zZSB0aGF0IGhhdmUgUlMgaW5mby4NCg0KIyBIRVJFOiBWZXJpZnkgU09TLVBlYWtfRU9TIE9EWQ0KDQpFUlJPUlMhIERvdWJsZS1jaGVjayBhbmQgc2VuZCB0byBCZWE6DQoNCmBgYHtyfQ0KZGJfcmVzdXJ2X1JTX3Nob3J0X1BMT1QgJT4lIGZpbHRlcihTT1NfRE9ZID4gUGVha19ET1kpDQpkYl9yZXN1cnZfUlNfc2hvcnRfUExPVCAlPiUgZmlsdGVyKFBlYWtfRE9ZID4gRU9TX0RPWSkNCmRiX3Jlc3Vydl9SU19zaG9ydF9QTE9UICU+JSBmaWx0ZXIoU09TX0RPWSA+IEVPU19ET1kpDQpgYGANCmBgYHtyfQ0KZGJfcmVzdXJ2X1JTX3Nob3J0X1BMT1QgJT4lIGZpbHRlcihORFZJX2F0X1BlYWsgPCBORFZJX2F0X1NPUykNCmRiX3Jlc3Vydl9SU19zaG9ydF9QTE9UICU+JSBmaWx0ZXIoTkRWSV9hdF9QZWFrIDwgTkRWSV9hdF9FT1MpDQpgYGANCg0KIyBDb21wYXJpc29uIGRpZmZlcmVudGlhbCBHUFMgdnMuIG90aGVyIHBvaW50cw0KDQpgYGB7cn0NCiMgRGVmaW5lIGZ1bmN0aW9uDQpkaXN0cl9wbG90X0dQUyA8LSBmdW5jdGlvbihkYXRhLCB5X3ZhciwgeV9sYWJlbCkgew0KICBnZ3Bsb3QoZGF0YSA9IGRhdGEgJT4lDQogICAgICAgICAgIGZpbHRlcihFVU5JU2FfMSAlaW4lIGMoIlQiLCAiUiIsICJTIiwgIlEiKSkgJT4lDQogICAgICAgICAgIG11dGF0ZShHUFMgPSANCiAgICAgICAgICAgICAgICAgICAgaWZlbHNlKA0KICAgICAgICAgICAgICAgICAgICAgIGlzLm5hKGBMb2NhdGlvbiBtZXRob2RgKSB8DQogICAgICAgICAgICAgICAgICAgICAgICBgTG9jYXRpb24gbWV0aG9kYCAhPSAiTG9jYXRpb24gd2l0aCBkaWZmZXJlbnRpYWwgR1BTIiwNCiAgICAgICAgICAgICAgICAgICAgICAiT3RoZXIiLA0KICAgICAgICAgICAgICAgICAgICAgICJEaWZmZXJlbnRpYWwgR1BTIikpLA0KICAgICAgICAgYWVzKHggPSBFVU5JU2FfMV9kZXNjciwgeSA9ICEhc3ltKHlfdmFyKSwgZmlsbCA9IEVVTklTYV8xX2Rlc2NyKSkgKw0KICAgIGdlb21fZmxhdF92aW9saW4ocG9zaXRpb24gPSBwb3NpdGlvbl9udWRnZSh4ID0gMC4yLCB5ID0gMCksIGFscGhhID0gMC44KSArDQogICAgZ2VvbV9wb2ludChhZXMoeSA9ICEhc3ltKHlfdmFyKSwgY29sb3IgPSBFVU5JU2FfMV9kZXNjciksDQogICAgICAgICAgICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX2ppdHRlcih3aWR0aCA9IDAuMTUpLCBzaXplID0gMSwgYWxwaGEgPSAwLjI1KSArDQogICAgZ2VvbV9ib3hwbG90KHdpZHRoID0gMC4yLCBvdXRsaWVyLnNoYXBlID0gTkEsIGFscGhhID0gMC41KSArDQogICAgc3RhdF9zdW1tYXJ5KGZ1bi55ID0gbWVhbiwgZ2VvbSA9ICJwb2ludCIsIHNoYXBlID0gMjAsIHNpemUgPSAxKSArDQogICAgc3RhdF9zdW1tYXJ5KGZ1bi5kYXRhID0gZnVuY3Rpb24oeCkgZGF0YS5mcmFtZSh5ID0gbWF4KHgpICsgMC4xLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWwgPSBsZW5ndGgoeCkpLA0KICAgICAgICAgICAgICAgICBnZW9tID0gInRleHQiLCBhZXMobGFiZWwgPSAuLmxhYmVsLi4pLCB2anVzdCA9IDAuNSkgKw0KICAgIGxhYnMoeSA9IHlfbGFiZWwsIHggPSAiRVVOSVMgbGV2ZWwgMSIpICsNCiAgICBzY2FsZV94X2Rpc2NyZXRlKGxhYmVscyA9IGZ1bmN0aW9uKHgpIHN0cl93cmFwKHgsIHdpZHRoID0gMTUpKSArDQogICAgZ3VpZGVzKGZpbGwgPSBGQUxTRSwgY29sb3IgPSBGQUxTRSkgKw0KICAgIHRoZW1lX2J3KCkgKyBjb29yZF9mbGlwKCkgKyBmYWNldF93cmFwKH4gR1BTKQ0KfQ0KYGBgDQoNCmBgYHtyfQ0KcGxvdF9kaXN0cl9ORFZJX0dQUyA8LSBkaXN0cl9wbG90X0dQUyhkYl9yZXN1cnZfUlNfc2hvcnRfUExPVCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk5EVklfbWF4IiwgIk5EVkkgbWF4IikNCnBsb3RfZGlzdHJfTkRNSV9HUFMgPC0gZGlzdHJfcGxvdF9HUFMoZGJfcmVzdXJ2X1JTX3Nob3J0X1BMT1QsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJORE1JX21heCIsICJORE1JIG1heCIpDQpwbG90X2Rpc3RyX05EV0lfR1BTIDwtIGRpc3RyX3Bsb3RfR1BTKGRiX3Jlc3Vydl9SU19zaG9ydF9QTE9ULA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTkRXSV9tYXgiLCAiTkRXSSBtYXgiKQ0KcGxvdF9kaXN0cl9TQVZJX0dQUyA8LSBkaXN0cl9wbG90X0dQUyhkYl9yZXN1cnZfUlNfc2hvcnRfUExPVCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlNBVklfbWF4IiwgIlNBVkkgbWF4IikNCnBsb3RfZGlzdHJfRVZJX0dQUyA8LSBkaXN0cl9wbG90X0dQUyhkYl9yZXN1cnZfUlNfc2hvcnRfUExPVCAlPiUNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbHRlcihFVklfbWF4IDw9IDEpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRVZJX21heCIsICJFVkkgbWF4IikNCnBsb3RfZGlzdHJfTkRWSV9HUFMNCnBsb3RfZGlzdHJfTkRNSV9HUFMNCnBsb3RfZGlzdHJfTkRXSV9HUFMNCnBsb3RfZGlzdHJfU0FWSV9HUFMNCnBsb3RfZGlzdHJfRVZJX0dQUw0KYGBgDQoNCiMgSW5jbHVkaW5nIFBMT1QgKFVTRSBMQVRFUj8pDQoNClN1bW1hcml6ZSB2YXJpYWJsZXMgYnkgcGxvdDoNCg0KYGBge3J9DQpkYl9yZXN1cnZfUlNfc2hvcnRfUExPVF9zdW1tIDwtIGRiX3Jlc3Vydl9SU19zaG9ydF9QTE9UICU+JQ0KICBmaWx0ZXIoRVVOSVNhXzEgJWluJSBjKCJUIiwgIlIiLCAiUyIsICJRIikpICU+JQ0KICBmaWx0ZXIoUzJfZGF0YSA9PSBUIHwgTGFuZHNhdF9kYXRhID09IFQpICU+JQ0KICBncm91cF9ieShQTE9UKSAlPiUNCiAgc3VtbWFyaXplKEVVTklTMSA9IGlmX2Vsc2Uobl9kaXN0aW5jdChFVU5JU2FfMSkgPiAxLCAiQ2hhbmdlIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdW5pcXVlKEVVTklTYV8xKVsxXSksDQogICAgICAgICAgICBjb3VudCA9IG4oKSwNCiAgICAgICAgICAgIGFjcm9zcyhzdGFydHNfd2l0aCgiTkRWSSIpLCBsaXN0KG1lYW4gPSBtZWFuLCBzZCA9IHNkKSwNCiAgICAgICAgICAgICAgICAgICAubmFtZXMgPSAie2NvbH1fe2ZufSIpKQ0KDQpgYGANCg0KTWF5YmUgdXNlIGxhdGVyIGJlY2F1c2Ugbm93IG1hbnkgcGxvdHMgaGF2ZSBvbmx5IG9uZSBvYnNlcnZhdGlvbiwgcHJvYmFibHkgYmVjYXVzZSBzb21lIExhbmRzYXQgZGF0YSBpcyBtaXNzaW5nPw0KDQojIEZpcnN0IHZhbGlkYXRpb24NCg0KRm9yIFQsIFIsIFMsIFEgaGFiaXRhdHMuDQoNCkRlZmluZSBhIHNldCBvZiBydWxlcyBmb3IgYSBmaXJzdCB2YWxpZGF0aW9uIG9mIEFMTCBSZVN1cnZleSBkYXRhLiBXZSBjYW4gY2FsbCB0aGVzZSAiRXhwZXJ0LWJhc2VkIiBydWxlcy4NCg0KTnVtYmVyIG9mIG9ic2VydmF0aW9ucyBpbiBSZVN1cnZleSBmcm9tIHRoZSBoYWJpdGF0cyBvZiBpbnRlcmVzdDoNCg0KYGBge3J9DQpucm93KGRiX3Jlc3Vydl9SU19zaG9ydF9QTE9UICU+JQ0KICAgICAgIGZpbHRlcihFVU5JU2FfMSAlaW4lIGMoIlQiLCAiUiIsICJTIiwgIlEiKSkpDQpgYGANCg0KTnVtYmVyIG9mIG9ic2VydmF0aW9ucyBpbiBSZVN1cnZleSBmcm9tIHRoZSBoYWJpdGF0cyBvZiBpbnRlcmVzdCBhbmQgd2l0aCBhbGwgUlMgZGF0YToNCg0KYGBge3J9DQpucm93KGRiX3Jlc3Vydl9SU19zaG9ydF9QTE9UICU+JQ0KICAgICAgIGZpbHRlcihFVU5JU2FfMSAlaW4lIGMoIlQiLCAiUiIsICJTIiwgIlEiKSkgJT4lDQogICAgICAgZmlsdGVyKENIX2RhdGEgPT0gVCkgJT4lDQogICAgICAgZmlsdGVyKFMyX2RhdGEgPT0gVCB8IExhbmRzYXRfZGF0YSA9PVQpICU+JQ0KICAgICAgIGZpbHRlcihTMl9waGVuX2RhdGEgPT0gVCkpDQpgYGANCg0KYGBge3J9DQpkYl9yZXN1cnZfUlNfc2hvcnRfUExPVF90ZXJyZXN0cmlhbCA8LSBkYl9yZXN1cnZfUlNfc2hvcnRfUExPVCAlPiUNCiAgZmlsdGVyKEVVTklTYV8xICVpbiUgYygiVCIsICJSIiwgIlMiLCAiUSIpKQ0KYGBgDQoNCiMjIERlZmluZSBydWxlcw0KDQpDcmVhdGUgY29sdW1uIGZvciBmaXJzdCB2YWxpZGF0aW9uIGJhc2VkIG9uIGRpZmZlcmVudCBpbmRpY2F0b3JzLCB3aGVyZSAid3JvbmciIGlzIG5vdGVkIHdoZW4gdGhlIHZhbGlkYXRpb24gcnVsZSBpcyBub3QgbWV0LiBJbmNsdWRlIEVVTklTMSBjb25mdXNpb25zLg0KDQpgYGB7cn0NCmRiX3Jlc3Vydl9SU19zaG9ydF9QTE9UX3RlcnJlc3RyaWFsICU+JSBjb3VudChFVU5JU2FfMSwgRVVOSVMxX2NvbmZfdHlwZSkNCmBgYA0KDQpEZWZpbmUgcnVsZXM6DQoNCmBgYHtyfQ0KZGJfcmVzdXJ2X1JTX3Nob3J0X1BMT1RfdGVycmVzdHJpYWwgPC0NCiAgZGJfcmVzdXJ2X1JTX3Nob3J0X1BMT1RfdGVycmVzdHJpYWwgJT4lDQogIG11dGF0ZSgNCiAgICB2YWxpZF8xX05EV0kgPSBjYXNlX3doZW4oDQogICAgICAjIFBvaW50cyB0aGF0IGFyZSBiYXNpY2FsbHkgd2F0ZXINCiAgICAgIE5EV0lfbWF4ID4gMC4zIH4gIndyb25nIiwNCiAgICAgIFRSVUUgfiBOQV9jaGFyYWN0ZXJfKSwNCiAgICB2YWxpZF8xX0NIID0gY2FzZV93aGVuKA0KICAgICAgIyBUIHBvaW50cyB3aXRoIGxvdyBDSA0KICAgICAgRVVOSVNhXzEgPT0gIlQiICYgY2Fub3B5X2hlaWdodCA8IDggfiAid3JvbmciLA0KICAgICAgIyBTIHBvaW50cyB3aXRoIGxvdyBDSA0KICAgICAgRVVOSVNhXzEgPT0iUyIgJiBjYW5vcHlfaGVpZ2h0IDwgNSB+ICJ3cm9uZyIsDQogICAgICAjIFIgJiBRIHBvaW50cyB3aXRoIGhpZ2ggQ0gNCiAgICAgIEVVTklTYV8xICVpbiUgYygiUiIsICJRIikgJiBjYW5vcHlfaGVpZ2h0ID4gMiB+ICJ3cm9uZyIsDQogICAgICBUUlVFIH4gTkFfY2hhcmFjdGVyXyksDQogICAgdmFsaWRfMV9ORFZJID0gY2FzZV93aGVuKA0KICAgICAgIyBUIHBvaW50cyB3aXRoIGxvdyBORFZJX21heA0KICAgICAgRVVOSVNhXzEgPT0gIlQiICYgTkRWSV9tYXggPCAwLjYgfiAid3JvbmciLA0KICAgICAgIyBTLVItUSBwb2ludHMgd2l0aCBsb3cgTkRWSV9tYXgNCiAgICAgIEVVTklTYV8xICVpbiUgYygiUiIsICJTIiwgIlEiKSAmIE5EVklfbWF4IDwgMC4yIH4gIndyb25nIiwNCiAgICAgIFRSVUUgfiBOQV9jaGFyYWN0ZXJfKSwNCiAgICAjIENvdW50IGhvdyBtYW55IHZhbGlkYXRpb24gcnVsZXMgYXJlIG5vdCBtZXQNCiAgICB2YWxpZF8xX2NvdW50ID0gcm93U3VtcyhhY3Jvc3MoYyh2YWxpZF8xX05EV0ksIHZhbGlkXzFfQ0gsIHZhbGlkXzFfTkRWSSksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICB+IC4gPT0gIndyb25nIiksIG5hLnJtID0gVFJVRSksDQogICAgIyBQb2ludHMgd2hlcmUgYXQgbGVhc3QgMSBydWxlIG5vdCBtZXQNCiAgICB2YWxpZF8xID0gaWZfZWxzZSh2YWxpZF8xX2NvdW50ID4gMCwgIkF0IGxlYXN0IDEgcnVsZSBicm9rZW4iLA0KICAgICAgICAgICAgICAgICAgICAgICJObyBydWxlcyBicm9rZW4gc28gZmFyIikNCiAgICApDQpgYGANCg0KIyMgUGxvdHMgZmlyc3QgdmFsaWRhdGlvbg0KDQpgYGB7cn0NCmdncGxvdChkYl9yZXN1cnZfUlNfc2hvcnRfUExPVF90ZXJyZXN0cmlhbCU+JQ0KICAgICAgICAgbXV0YXRlKHJ1bGVzX2Jyb2tlbiA9IGNhc2Vfd2hlbigNCiAgICAgICAgICAgdmFsaWRfMV9jb3VudCA9PSAxICYgdmFsaWRfMV9ORFdJID09ICJ3cm9uZyIgfiAiTkRXSSIsDQogICAgICAgICAgIHZhbGlkXzFfY291bnQgPT0gMSAmIHZhbGlkXzFfTkRWSSA9PSAid3JvbmciIH4gIk5EVkkiLA0KICAgICAgICAgICB2YWxpZF8xX2NvdW50ID09IDEgJiB2YWxpZF8xX0NIID09ICJ3cm9uZyIgfiAiQ0giLA0KICAgICAgICAgICB2YWxpZF8xX2NvdW50ID09IDIgJg0KICAgICAgICAgICAgIHZhbGlkXzFfTkRXSSA9PSAid3JvbmciICYgdmFsaWRfMV9ORFZJID09ICJ3cm9uZyJ+ICJORFdJICsgTkRWSSIsDQogICAgICAgICAgIHZhbGlkXzFfY291bnQgPT0gMiAmDQogICAgICAgICAgICAgdmFsaWRfMV9ORFdJID09ICJ3cm9uZyIgJiB2YWxpZF8xX0NIID09ICJ3cm9uZyJ+ICJORFdJICsgQ0giLA0KICAgICAgICAgICB2YWxpZF8xX2NvdW50ID09IDIgJg0KICAgICAgICAgICAgIHZhbGlkXzFfTkRWSSA9PSAid3JvbmciICYgdmFsaWRfMV9DSCA9PSAid3JvbmcifiAiTkRWSSArIENIIiwNCiAgICAgICAgICAgdmFsaWRfMV9jb3VudCA9PSAzIH4gIk5EV0kgKyBORFZJICsgQ0giLA0KICAgICAgICAgICBUUlVFIH4gTkFfY2hhcmFjdGVyXw0KICAgICAgICAgKSksIA0KICAgICAgIGFlcyh4ID0gdmFsaWRfMV9jb3VudCwgZmlsbCA9IHJ1bGVzX2Jyb2tlbikpICsNCiAgZ2VvbV9iYXIoKSArIGxhYnMoeCA9ICJOdW1iZXIgb2YgYnJva2VuIHJ1bGVzIikNCmBgYA0KDQpgYGB7cn0NCmRiX3Jlc3Vydl9SU19zaG9ydF9QTE9UX3RlcnJlc3RyaWFsICU+JQ0KICAgICAgICAgbXV0YXRlKHJ1bGVzX2Jyb2tlbiA9IGNhc2Vfd2hlbigNCiAgICAgICAgICAgdmFsaWRfMV9jb3VudCA9PSAxICYgdmFsaWRfMV9ORFdJID09ICJ3cm9uZyIgfiAiTkRXSSIsDQogICAgICAgICAgIHZhbGlkXzFfY291bnQgPT0gMSAmIHZhbGlkXzFfTkRWSSA9PSAid3JvbmciIH4gIk5EVkkiLA0KICAgICAgICAgICB2YWxpZF8xX2NvdW50ID09IDEgJiB2YWxpZF8xX0NIID09ICJ3cm9uZyIgfiAiQ0giLA0KICAgICAgICAgICB2YWxpZF8xX2NvdW50ID09IDIgJg0KICAgICAgICAgICAgIHZhbGlkXzFfTkRXSSA9PSAid3JvbmciICYgdmFsaWRfMV9ORFZJID09ICJ3cm9uZyJ+ICJORFdJICsgTkRWSSIsDQogICAgICAgICAgIHZhbGlkXzFfY291bnQgPT0gMiAmDQogICAgICAgICAgICAgdmFsaWRfMV9ORFdJID09ICJ3cm9uZyIgJiB2YWxpZF8xX0NIID09ICJ3cm9uZyJ+ICJORFdJICsgQ0giLA0KICAgICAgICAgICB2YWxpZF8xX2NvdW50ID09IDIgJg0KICAgICAgICAgICAgIHZhbGlkXzFfTkRWSSA9PSAid3JvbmciICYgdmFsaWRfMV9DSCA9PSAid3JvbmcifiAiTkRWSSArIENIIiwNCiAgICAgICAgICAgdmFsaWRfMV9jb3VudCA9PSAzIH4gIk5EV0kgKyBORFZJICsgQ0giLA0KICAgICAgICAgICBUUlVFIH4gTkFfY2hhcmFjdGVyXw0KICAgICAgICAgKSkgJT4lDQogIGNvdW50KHJ1bGVzX2Jyb2tlbiwgRVVOSVMxX2NvbmZfdHlwZSkNCmBgYA0KDQpQcm9wb3J0aW9uIG9mIG9ic2VydmF0aW9ucyBub3QgdmFsaWRhdGVkIChzbyBmYXIpOg0KDQpgYGB7cn0NCm5yb3coZGJfcmVzdXJ2X1JTX3Nob3J0X1BMT1RfdGVycmVzdHJpYWwgJT4lIGZpbHRlcih2YWxpZF8xX2NvdW50ID4gMCkpLw0KICBucm93KGRiX3Jlc3Vydl9SU19zaG9ydF9QTE9UX3RlcnJlc3RyaWFsKQ0KYGBgDQoNCkJ1dCBiZSBhd2FyZSB0aGF0IHRoZXJlIGFyZSBzdGlsbCBtYW55IG1pc3NpbmcgUlMgZGF0YSwgZS5nLiBMYW5kc2F0IGRhdGEgZm9yIGJpb3JlZ2lvbiBDT04gKG1hbnkgcG9pbnRzKS4NCg0KYGBge3J9DQpnZ3Bsb3QoZGJfcmVzdXJ2X1JTX3Nob3J0X1BMT1RfdGVycmVzdHJpYWwgJT4lDQogICAgICAgICBtdXRhdGUoZGlmZl9HUFMgPSBpZl9lbHNlKA0KICAgICAgICAgICBgTG9jYXRpb24gbWV0aG9kYCAhPSAiTG9jYXRpb24gd2l0aCBkaWZmZXJlbnRpYWwgR1BTIiB8DQogICAgICAgICAgICAgaXMubmEoYExvY2F0aW9uIG1ldGhvZGApLCAibm8iLCAieWVzIikpLCANCiAgICAgICBhZXMoeCA9IGRpZmZfR1BTLCBmaWxsID0gdmFsaWRfMSkpICsNCiAgZ2VvbV9iYXIoKSArIGxhYnMoeCA9ICJEaWZmZXJlbnRpYWwgR1BTIikNCmdncGxvdChkYl9yZXN1cnZfUlNfc2hvcnRfUExPVF90ZXJyZXN0cmlhbCAlPiUNCiAgICAgICAgIG11dGF0ZShHUFMgPSBjYXNlX3doZW4oDQogICAgICAgICAgIGBMb2NhdGlvbiBtZXRob2RgID09ICJMb2NhdGlvbiB3aXRoIGRpZmZlcmVudGlhbCBHUFMiIH4gInllcyIsDQogICAgICAgICAgIGBMb2NhdGlvbiBtZXRob2RgID09ICJMb2NhdGlvbiB3aXRoIEdQUyIgfiAieWVzIiwNCiAgICAgICAgICAgaXMubmEoYExvY2F0aW9uIG1ldGhvZGApIH4gIm5vIiwNCiAgICAgICAgICAgVFJVRSB+ICJubyINCiAgICAgICAgICkpLCANCiAgICAgICBhZXMoeCA9IEdQUywgZmlsbCA9IHZhbGlkXzEpKSArDQogIGdlb21fYmFyKCkgKyBsYWJzKHggPSAiR1BTIikNCmBgYA0KDQpQb2ludHMgd2l0aCBhbnkgcnVsZSBicm9rZW4gYW5kIGNvbmZ1c2lvbiBiZXR3ZWVuIEVVTklTOg0KDQpgYGB7cn0NCm5yb3coZGJfcmVzdXJ2X1JTX3Nob3J0X1BMT1RfdGVycmVzdHJpYWwgJT4lDQogICAgICAgZmlsdGVyKEVVTklTMV9jb25mID09IFQgJiB2YWxpZF8xX2NvdW50ID4gMCkpDQpgYGANCg0KQ29udmVydCB0byBzaHAgdG8gbG9vayBhdCB0aGVzZSBpbiBHSVM6DQoNCmBgYHtyfQ0KIyBzdF93cml0ZShkYl9yZXN1cnZfUlNfc2hvcnRfUExPVF90ZXJyZXN0cmlhbCAlPiUNCiMgICAgICAgICAgICBmaWx0ZXIoRVVOSVMxX2NvbmYgPT0gVCAmIHZhbGlkXzFfY291bnQgPiAwKSAlPiUNCiMgICAgICAgICAgICBzdF9hc19zZihjb29yZHMgPSBjKCJMb25fdXBkYXRlZCIsICJMYXRfdXBkYXRlZCIpLCBjcnMgPSA0MzI2KSwNCiMgICAgICAgICAgIkM6L0dJUy9NT1RJVkFURS9zaGFwZWZpbGVzL3Jlc3Vydl9ub3RfdmFsX0VVTklTX2NvbmYuc2hwIikNCmBgYA0KDQpDaGVja2VkIGFuZCB5ZXMNCg0KSG93IG1hbnkgcG9pbnRzIHdpdGggZGlmZmVyZW50aWFsIEdQUyB0aGF0IGhhdmUgYXQgbGVhc3QgMSBydWxlIGJyb2tlbj8NCg0KYGBge3J9DQpucm93KGRiX3Jlc3Vydl9SU19zaG9ydF9QTE9UX3RlcnJlc3RyaWFsICU+JQ0KICBmaWx0ZXIoYExvY2F0aW9uIG1ldGhvZGAgPT0gIkxvY2F0aW9uIHdpdGggZGlmZmVyZW50aWFsIEdQUyIgJg0KICAgICAgICAgICB2YWxpZF8xID09ICJBdCBsZWFzdCAxIHJ1bGUgYnJva2VuIikpDQpgYGANCg0KQ29udmVydCB0byBzaHAgdG8gbG9vayBhdCB0aGVzZSBpbiBHSVM6DQoNCmBgYHtyfQ0KIyBzdF93cml0ZShkYl9yZXN1cnZfUlNfc2hvcnRfUExPVF90ZXJyZXN0cmlhbCAlPiUNCiMgICAgICAgICAgICBmaWx0ZXIoYExvY2F0aW9uIG1ldGhvZGAgPT0gIkxvY2F0aW9uIHdpdGggZGlmZmVyZW50aWFsIEdQUyIgJg0KIyAgICAgICAgICAgICAgICAgICAgIHZhbGlkXzEgPT0gIkF0IGxlYXN0IDEgcnVsZSBicm9rZW4iKSAlPiUNCiMgICAgICAgICAgICBzdF9hc19zZihjb29yZHMgPSBjKCJMb25fdXBkYXRlZCIsICJMYXRfdXBkYXRlZCIpLCBjcnMgPSA0MzI2KSwNCiMgICAgICAgICAgIkM6L0dJUy9NT1RJVkFURS9zaGFwZWZpbGVzL3Jlc3Vydl9ub3RfdmFsX2RpZmZfR1BTLnNocCIpDQpgYGANCg0KIyBEaXN0cmlidXRpb25zIGZyb20gZGlmZiBHUFMgcG9pbnRzIHdpdGhvdXQgcnVsZXMgYnJva2VuIHNvIGZhcg0KDQpDcmVhdGUgdGliYmxlIHdpdGggZGlmZmVyZW50aWFsIEdQUyBwb2ludHMgd2l0aG91dCBydWxlcyBicm9rZW4gc28gZmFyOg0KDQpgYGB7cn0NCmRpZmZfR1BTX3ZhbGlkIDwtIGRiX3Jlc3Vydl9SU19zaG9ydF9QTE9UX3RlcnJlc3RyaWFsICU+JQ0KICBmaWx0ZXIoYExvY2F0aW9uIG1ldGhvZGAgPT0gIkxvY2F0aW9uIHdpdGggZGlmZmVyZW50aWFsIEdQUyIgJg0KICAgICAgICAgICB2YWxpZF8xID09ICJObyBydWxlcyBicm9rZW4gc28gZmFyIikgDQpgYGANCg0KIyMgTWF4LiBhbmQgbWluLiBORFZJLCBORE1JLCBORFdJLCBTQVZJIGFuZCBFVkkNCg0KYGBge3J9DQpwbG90X2Rpc3RyX05EVklfbWF4X2RpZmZfR1BTX3ZhbGlkIDwtIGRpc3RyX3Bsb3QoZGlmZl9HUFNfdmFsaWQsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTkRWSV9tYXgiLCAiTkRWSSBtYXgiKQ0KcGxvdF9kaXN0cl9ORE1JX21heF9kaWZmX0dQU192YWxpZCA8LSBkaXN0cl9wbG90KGRpZmZfR1BTX3ZhbGlkLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk5ETUlfbWF4IiwgIk5ETUkgbWF4IikNCnBsb3RfZGlzdHJfTkRXSV9tYXhfZGlmZl9HUFNfdmFsaWQgPC0gZGlzdHJfcGxvdChkaWZmX0dQU192YWxpZCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJORFdJX21heCIsICJORFdJIG1heCIpDQpwbG90X2Rpc3RyX1NBVklfbWF4X2RpZmZfR1BTX3ZhbGlkIDwtIGRpc3RyX3Bsb3QoZGlmZl9HUFNfdmFsaWQsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiU0FWSV9tYXgiLCAiU0FWSSBtYXgiKQ0KcGxvdF9kaXN0cl9FVklfbWF4X2RpZmZfR1BTX3ZhbGlkIDwtIGRpc3RyX3Bsb3QoZGlmZl9HUFNfdmFsaWQgJT4lDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsdGVyKEVWSV9tYXggPD0gMSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJFVklfbWF4IiwgIkVWSSBtYXgiKQ0KcGxvdF9kaXN0cl9ORFZJX21heF9kaWZmX0dQU192YWxpZA0KcGxvdF9kaXN0cl9ORE1JX21heF9kaWZmX0dQU192YWxpZA0KcGxvdF9kaXN0cl9ORFdJX21heF9kaWZmX0dQU192YWxpZA0KcGxvdF9kaXN0cl9TQVZJX21heF9kaWZmX0dQU192YWxpZA0KcGxvdF9kaXN0cl9FVklfbWF4X2RpZmZfR1BTX3ZhbGlkDQpgYGANCg0KYGBge3J9DQpwbG90X2Rpc3RyX05EVklfbWluX2RpZmZfR1BTX3ZhbGlkIDwtIGRpc3RyX3Bsb3QoZGlmZl9HUFNfdmFsaWQsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTkRWSV9taW4iLCAiTkRWSSBtaW4iKQ0KcGxvdF9kaXN0cl9ORE1JX21pbl9kaWZmX0dQU192YWxpZCA8LSBkaXN0cl9wbG90KGRpZmZfR1BTX3ZhbGlkLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk5ETUlfbWluIiwgIk5ETUkgbWluIikNCnBsb3RfZGlzdHJfTkRXSV9taW5fZGlmZl9HUFNfdmFsaWQgPC0gZGlzdHJfcGxvdChkaWZmX0dQU192YWxpZCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJORFdJX21pbiIsICJORFdJIG1pbiIpDQpwbG90X2Rpc3RyX1NBVklfbWluX2RpZmZfR1BTX3ZhbGlkIDwtIGRpc3RyX3Bsb3QoZGlmZl9HUFNfdmFsaWQsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiU0FWSV9taW4iLCAiU0FWSSBtaW4iKQ0KcGxvdF9kaXN0cl9FVklfbWluX2RpZmZfR1BTX3ZhbGlkIDwtIGRpc3RyX3Bsb3QoZGlmZl9HUFNfdmFsaWQgJT4lDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgU29tZSB2YWx1ZXMgd3JvbmchDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgRVZJIHNob3VsZCBub3QgYmUgbG93ZXIgdGhhbiAtMQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWx0ZXIoRVZJX21pbiA+PSAtMSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRVZJX21pbiIsICJFVkkgbWluIikNCnBsb3RfZGlzdHJfTkRWSV9taW5fZGlmZl9HUFNfdmFsaWQNCnBsb3RfZGlzdHJfTkRNSV9taW5fZGlmZl9HUFNfdmFsaWQNCnBsb3RfZGlzdHJfTkRXSV9taW5fZGlmZl9HUFNfdmFsaWQNCnBsb3RfZGlzdHJfU0FWSV9taW5fZGlmZl9HUFNfdmFsaWQNCnBsb3RfZGlzdHJfRVZJX21pbl9kaWZmX0dQU192YWxpZA0KYGBgDQoNCiMjIENIDQoNCmBgYHtyfQ0KcGxvdF9kaXN0cl9DSF9kaWZmX0dQU192YWxpZCA8LSBkaXN0cl9wbG90KGRpZmZfR1BTX3ZhbGlkLCAiY2Fub3B5X2hlaWdodCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkNhbm9weSBoZWlnaHQgKG0pIikNCnBsb3RfZGlzdHJfQ0hfZGlmZl9HUFNfdmFsaWQNCmBgYA0KDQojIyBQaGVub2xvZ3kNCg0KYGBge3J9DQpwbG90X2Rpc3RyX1NPU19ET1lfZGlmZl9HUFNfdmFsaWQgPC0gZGlzdHJfcGxvdChkaWZmX0dQU192YWxpZCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJTT1NfRE9ZIiwgIlNPUyBET1kiKQ0KcGxvdF9kaXN0cl9QZWFrX0RPWV9kaWZmX0dQU192YWxpZCA8LSBkaXN0cl9wbG90KGRpZmZfR1BTX3ZhbGlkLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJQZWFrX0RPWSIsICJQZWFrIERPWSIpDQpwbG90X2Rpc3RyX0VPU19ET1lfZGlmZl9HUFNfdmFsaWQgPC0gZGlzdHJfcGxvdChkaWZmX0dQU192YWxpZCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJFT1NfRE9ZIiwgIkVPUyBET1kiKQ0KcGxvdF9kaXN0cl9TT1NfRE9ZX2RpZmZfR1BTX3ZhbGlkDQpwbG90X2Rpc3RyX1BlYWtfRE9ZX2RpZmZfR1BTX3ZhbGlkDQpwbG90X2Rpc3RyX0VPU19ET1lfZGlmZl9HUFNfdmFsaWQNCmBgYA0KDQpOb3QgdmVyeSBjb25jbHVzaXZlLi4uDQoNCiMgRGlzdHJpYnV0aW9ucyBmcm9tIEdQUyAoZGlmZiBvciBub3QpIHBvaW50cyB3aXRob3V0IHJ1bGVzIGJyb2tlbiBzbyBmYXINCg0KQ3JlYXRlIHRpYmJsZSB3aXRoIGRpZmZlcmVudGlhbCBHUFMgcG9pbnRzIHdpdGhvdXQgcnVsZXMgYnJva2VuIHNvIGZhcjoNCg0KYGBge3J9DQphbGxfR1BTX3ZhbGlkIDwtIGRiX3Jlc3Vydl9SU19zaG9ydF9QTE9UX3RlcnJlc3RyaWFsICU+JQ0KICBmaWx0ZXIoKGBMb2NhdGlvbiBtZXRob2RgID09ICJMb2NhdGlvbiB3aXRoIGRpZmZlcmVudGlhbCBHUFMiIHwgDQogICAgICAgICAgICBgTG9jYXRpb24gbWV0aG9kYCA9PSAiTG9jYXRpb24gd2l0aCBHUFMiICkgJg0KICAgICAgICAgICB2YWxpZF8xID09ICJObyBydWxlcyBicm9rZW4gc28gZmFyIikgDQpgYGANCg0KIyMgTWF4LiBhbmQgbWluLiBORFZJLCBORE1JLCBORFdJLCBTQVZJIGFuZCBFVkkNCg0KYGBge3J9DQpwbG90X2Rpc3RyX05EVklfbWF4X2FsbF9HUFNfdmFsaWQgPC0gZGlzdHJfcGxvdChhbGxfR1BTX3ZhbGlkLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk5EVklfbWF4IiwgIk5EVkkgbWF4IikNCnBsb3RfZGlzdHJfTkRNSV9tYXhfYWxsX0dQU192YWxpZCA8LSBkaXN0cl9wbG90KGFsbF9HUFNfdmFsaWQsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTkRNSV9tYXgiLCAiTkRNSSBtYXgiKQ0KcGxvdF9kaXN0cl9ORFdJX21heF9hbGxfR1BTX3ZhbGlkIDwtIGRpc3RyX3Bsb3QoYWxsX0dQU192YWxpZCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJORFdJX21heCIsICJORFdJIG1heCIpDQpwbG90X2Rpc3RyX1NBVklfbWF4X2FsbF9HUFNfdmFsaWQgPC0gZGlzdHJfcGxvdChhbGxfR1BTX3ZhbGlkLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlNBVklfbWF4IiwgIlNBVkkgbWF4IikNCnBsb3RfZGlzdHJfRVZJX21heF9hbGxfR1BTX3ZhbGlkIDwtIGRpc3RyX3Bsb3QoYWxsX0dQU192YWxpZCAlPiUNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWx0ZXIoRVZJX21heCA8PSAxKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkVWSV9tYXgiLCAiRVZJIG1heCIpDQpwbG90X2Rpc3RyX05EVklfbWF4X2FsbF9HUFNfdmFsaWQNCnBsb3RfZGlzdHJfTkRNSV9tYXhfYWxsX0dQU192YWxpZA0KcGxvdF9kaXN0cl9ORFdJX21heF9hbGxfR1BTX3ZhbGlkDQpwbG90X2Rpc3RyX1NBVklfbWF4X2FsbF9HUFNfdmFsaWQNCnBsb3RfZGlzdHJfRVZJX21heF9hbGxfR1BTX3ZhbGlkDQpgYGANCg0KYGBge3J9DQpwbG90X2Rpc3RyX05EVklfbWluX2FsbF9HUFNfdmFsaWQgPC0gZGlzdHJfcGxvdChhbGxfR1BTX3ZhbGlkLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk5EVklfbWluIiwgIk5EVkkgbWluIikNCnBsb3RfZGlzdHJfTkRNSV9taW5fYWxsX0dQU192YWxpZCA8LSBkaXN0cl9wbG90KGFsbF9HUFNfdmFsaWQsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTkRNSV9taW4iLCAiTkRNSSBtaW4iKQ0KcGxvdF9kaXN0cl9ORFdJX21pbl9hbGxfR1BTX3ZhbGlkIDwtIGRpc3RyX3Bsb3QoYWxsX0dQU192YWxpZCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJORFdJX21pbiIsICJORFdJIG1pbiIpDQpwbG90X2Rpc3RyX1NBVklfbWluX2FsbF9HUFNfdmFsaWQgPC0gZGlzdHJfcGxvdChhbGxfR1BTX3ZhbGlkLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlNBVklfbWluIiwgIlNBVkkgbWluIikNCnBsb3RfZGlzdHJfRVZJX21pbl9hbGxfR1BTX3ZhbGlkIDwtIGRpc3RyX3Bsb3QoYWxsX0dQU192YWxpZCAlPiUNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBTb21lIHZhbHVlcyB3cm9uZyENCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBFVkkgc2hvdWxkIG5vdCBiZSBsb3dlciB0aGFuIC0xDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbHRlcihFVklfbWluID49IC0xKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJFVklfbWluIiwgIkVWSSBtaW4iKQ0KcGxvdF9kaXN0cl9ORFZJX21pbl9hbGxfR1BTX3ZhbGlkDQpwbG90X2Rpc3RyX05ETUlfbWluX2FsbF9HUFNfdmFsaWQNCnBsb3RfZGlzdHJfTkRXSV9taW5fYWxsX0dQU192YWxpZA0KcGxvdF9kaXN0cl9TQVZJX21pbl9hbGxfR1BTX3ZhbGlkDQpwbG90X2Rpc3RyX0VWSV9taW5fYWxsX0dQU192YWxpZA0KYGBgDQoNCiMjIENIDQoNCmBgYHtyfQ0KcGxvdF9kaXN0cl9DSF9hbGxfR1BTX3ZhbGlkIDwtIGRpc3RyX3Bsb3QoYWxsX0dQU192YWxpZCwgImNhbm9weV9oZWlnaHQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJDYW5vcHkgaGVpZ2h0IChtKSIpDQpwbG90X2Rpc3RyX0NIX2FsbF9HUFNfdmFsaWQNCmBgYA0KDQojIyBQaGVub2xvZ3kNCg0KYGBge3J9DQpwbG90X2Rpc3RyX1NPU19ET1lfYWxsX0dQU192YWxpZCA8LSBkaXN0cl9wbG90KGFsbF9HUFNfdmFsaWQsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiU09TX0RPWSIsICJTT1MgRE9ZIikNCnBsb3RfZGlzdHJfUGVha19ET1lfYWxsX0dQU192YWxpZCA8LSBkaXN0cl9wbG90KGFsbF9HUFNfdmFsaWQsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlBlYWtfRE9ZIiwgIlBlYWsgRE9ZIikNCnBsb3RfZGlzdHJfRU9TX0RPWV9hbGxfR1BTX3ZhbGlkIDwtIGRpc3RyX3Bsb3QoYWxsX0dQU192YWxpZCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJFT1NfRE9ZIiwgIkVPUyBET1kiKQ0KcGxvdF9kaXN0cl9TT1NfRE9ZX2FsbF9HUFNfdmFsaWQNCnBsb3RfZGlzdHJfUGVha19ET1lfYWxsX0dQU192YWxpZA0KcGxvdF9kaXN0cl9FT1NfRE9ZX2FsbF9HUFNfdmFsaWQNCmBgYA0KDQojIENvbWJpbmVkIHBsb3QNCg0KYGBge3J9DQpncmlkLmFycmFuZ2UoDQogIHBsb3RfZGlzdHJfTkRWSV9tYXhfZGlmZl9HUFNfdmFsaWQsIHBsb3RfZGlzdHJfTkRWSV9tYXhfYWxsX0dQU192YWxpZCwNCiAgcGxvdF9kaXN0cl9ORE1JX21pbl9kaWZmX0dQU192YWxpZCwgcGxvdF9kaXN0cl9ORE1JX21pbl9hbGxfR1BTX3ZhbGlkLA0KICBuY29sID0gMikNCmBgYA0KDQojIERpZmYgR1BTIHZhbGlkIHBvaW50cyBhYm92ZSBwMjAgb2YgTkRWSV9tYXggYW5kIE5ETUlfbWluIGZvciBlYWNoIGhhYml0YXQNCg0KYGBge3J9DQpwZXJjZW50aWxlc19kaWZmX0dQUyA8LSBkaWZmX0dQU192YWxpZCAlPiUNCiAgZ3JvdXBfYnkoRVVOSVNhXzEpICU+JQ0KICBzdW1tYXJpemUocGVyY2VudGlsZV8yMF9ORFZJX21heCA9IHF1YW50aWxlKE5EVklfbWF4LCBwcm9icyA9IDAuMjAsIG5hLnJtID0gVCksDQogICAgICAgICAgICBwZXJjZW50aWxlXzIwX05ETUlfbWluID0gcXVhbnRpbGUoTkRNSV9taW4sIHByb2JzID0gMC4yMCwgbmEucm0gPSBUKSkNCg0KZGlmZl9HUFNfdmFsaWQgPC0gZGlmZl9HUFNfdmFsaWQgJT4lDQogIGxlZnRfam9pbihwZXJjZW50aWxlc19kaWZmX0dQUywgYnkgPSAiRVVOSVNhXzEiKSAlPiUNCiAgbXV0YXRlKGNhdGVnb3J5X05EVklfbWF4ID0gY2FzZV93aGVuKA0KICAgIE5EVklfbWF4IDwgcGVyY2VudGlsZV8yMF9ORFZJX21heCB+ICJiZWxvd18yMHRoIiwNCiAgICBORFZJX21heCA+PSBwZXJjZW50aWxlXzIwX05EVklfbWF4IH4gImFib3ZlXzIwdGgiKSwNCiAgY2F0ZWdvcnlfTkRNSV9taW4gPSBjYXNlX3doZW4oDQogICAgTkRNSV9taW4gPCBwZXJjZW50aWxlXzIwX05ETUlfbWluIH4gImJlbG93XzIwdGgiLA0KICAgIE5ETUlfbWluID49IHBlcmNlbnRpbGVfMjBfTkRNSV9taW4gfiAiYWJvdmVfMjB0aCIpKQ0KDQpnZ3Bsb3QoZGF0YSA9IGRpZmZfR1BTX3ZhbGlkLA0KICAgICAgIGFlcyh4ID0gRVVOSVNhXzFfZGVzY3IsIHkgPSBORFZJX21heCkpICsNCiAgZ2VvbV9mbGF0X3Zpb2xpbihwb3NpdGlvbiA9IHBvc2l0aW9uX251ZGdlKHggPSAwLjIsIHkgPSAwKSwgYWxwaGEgPSAwLjgsDQogICAgICAgICAgICAgICAgICAgZmlsbCA9ICJsaWdodGJsdWUiKSArDQogIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gY2F0ZWdvcnlfTkRWSV9tYXgpLA0KICAgICAgICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25faml0dGVyKHdpZHRoID0gMC4xNSksIHNpemUgPSAxLCBhbHBoYSA9IDAuMjUpICsNCiAgZ2VvbV9ib3hwbG90KHdpZHRoID0gMC4yLCBvdXRsaWVyLnNoYXBlID0gTkEsIGFscGhhID0gMC41KSArDQogIHN0YXRfc3VtbWFyeShmdW4ueSA9IG1lYW4sIGdlb20gPSAicG9pbnQiLCBzaGFwZSA9IDIwLCBzaXplID0gMSkgKw0KICBzdGF0X3N1bW1hcnkoZnVuLmRhdGEgPSBmdW5jdGlvbih4KSBkYXRhLmZyYW1lKHkgPSBtYXgoeCkgKyAwLjEsIGxhYmVsID0gbGVuZ3RoKHgpKSwNCiAgICAgICAgICAgICAgIGdlb20gPSAidGV4dCIsIGFlcyhsYWJlbCA9IC4ubGFiZWwuLiksIHZqdXN0ID0gMC41KSArDQogIGxhYnMoeSA9ICJORFZJIG1heCIsIHggPSAiRVVOSVMgbGV2ZWwgMSIpICsNCiAgZ3VpZGVzKGZpbGwgPSBGQUxTRSwgY29sb3IgPSBGQUxTRSkgKw0KICBzY2FsZV94X2Rpc2NyZXRlKGxhYmVscyA9IGZ1bmN0aW9uKHgpIHN0cl93cmFwKHgsIHdpZHRoID0gMTUpKSArDQogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJiZWxvd18yMHRoIiA9ICJncmV5IiwgImFib3ZlXzIwdGgiID0gImxpZ2h0Ymx1ZSIpKSArDQogIHRoZW1lX2J3KCkgKyBjb29yZF9mbGlwKCkNCg0KZ2dwbG90KGRhdGEgPSBkaWZmX0dQU192YWxpZCwNCiAgICAgICBhZXMoeCA9IEVVTklTYV8xX2Rlc2NyLCB5ID0gTkRNSV9taW4pKSArDQogIGdlb21fZmxhdF92aW9saW4ocG9zaXRpb24gPSBwb3NpdGlvbl9udWRnZSh4ID0gMC4yLCB5ID0gMCksIGFscGhhID0gMC44LA0KICAgICAgICAgICAgICAgICAgIGZpbGwgPSAibGlnaHRibHVlIikgKw0KICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IGNhdGVnb3J5X05ETUlfbWluKSwNCiAgICAgICAgICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX2ppdHRlcih3aWR0aCA9IDAuMTUpLCBzaXplID0gMSwgYWxwaGEgPSAwLjI1KSArDQogIGdlb21fYm94cGxvdCh3aWR0aCA9IDAuMiwgb3V0bGllci5zaGFwZSA9IE5BLCBhbHBoYSA9IDAuNSkgKw0KICBzdGF0X3N1bW1hcnkoZnVuLnkgPSBtZWFuLCBnZW9tID0gInBvaW50Iiwgc2hhcGUgPSAyMCwgc2l6ZSA9IDEpICsNCiAgc3RhdF9zdW1tYXJ5KGZ1bi5kYXRhID0gZnVuY3Rpb24oeCkgZGF0YS5mcmFtZSh5ID0gbWF4KHgpICsgMC4xLCBsYWJlbCA9IGxlbmd0aCh4KSksDQogICAgICAgICAgICAgICBnZW9tID0gInRleHQiLCBhZXMobGFiZWwgPSAuLmxhYmVsLi4pLCB2anVzdCA9IDAuNSkgKw0KICBsYWJzKHkgPSAiTkRNSSBtaW4iLCB4ID0gIkVVTklTIGxldmVsIDEiKSArDQogIGd1aWRlcyhmaWxsID0gRkFMU0UsIGNvbG9yID0gRkFMU0UpICsNCiAgc2NhbGVfeF9kaXNjcmV0ZShsYWJlbHMgPSBmdW5jdGlvbih4KSBzdHJfd3JhcCh4LCB3aWR0aCA9IDE1KSkgKw0KICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiYmVsb3dfMjB0aCIgPSAiZ3JleSIsICJhYm92ZV8yMHRoIiA9ICJsaWdodGJsdWUiKSkgKw0KICB0aGVtZV9idygpICsgY29vcmRfZmxpcCgpDQpgYGANCg0KIyBHUFMgKGRpZmYgb3Igbm90KSB2YWxpZCBwb2ludHMgYWJvdmUgcDIwIG9mIE5EVklfbWF4IGFuZCBORE1JX21pbiBmb3IgZWFjaCBoYWJpdGF0DQoNCmBgYHtyfQ0KcGVyY2VudGlsZXNfYWxsX0dQUyA8LSBhbGxfR1BTX3ZhbGlkICU+JQ0KICBncm91cF9ieShFVU5JU2FfMSkgJT4lDQogIHN1bW1hcml6ZShwZXJjZW50aWxlXzIwX05EVklfbWF4ID0gcXVhbnRpbGUoTkRWSV9tYXgsIHByb2JzID0gMC4yMCwgbmEucm0gPSBUKSwNCiAgICAgICAgICAgIHBlcmNlbnRpbGVfMjBfTkRNSV9taW4gPSBxdWFudGlsZShORE1JX21pbiwgcHJvYnMgPSAwLjIwLCBuYS5ybSA9IFQpKQ0KDQphbGxfR1BTX3ZhbGlkIDwtIGFsbF9HUFNfdmFsaWQgJT4lDQogIGxlZnRfam9pbihwZXJjZW50aWxlc19hbGxfR1BTLCBieSA9ICJFVU5JU2FfMSIpICU+JQ0KICBtdXRhdGUoY2F0ZWdvcnlfTkRWSV9tYXggPSBjYXNlX3doZW4oDQogICAgTkRWSV9tYXggPCBwZXJjZW50aWxlXzIwX05EVklfbWF4IH4gImJlbG93XzIwdGgiLA0KICAgIE5EVklfbWF4ID49IHBlcmNlbnRpbGVfMjBfTkRWSV9tYXggfiAiYWJvdmVfMjB0aCIpLA0KICBjYXRlZ29yeV9ORE1JX21pbiA9IGNhc2Vfd2hlbigNCiAgICBORE1JX21pbiA8IHBlcmNlbnRpbGVfMjBfTkRNSV9taW4gfiAiYmVsb3dfMjB0aCIsDQogICAgTkRNSV9taW4gPj0gcGVyY2VudGlsZV8yMF9ORE1JX21pbiB+ICJhYm92ZV8yMHRoIikpDQoNCmdncGxvdChkYXRhID0gYWxsX0dQU192YWxpZCwNCiAgICAgICBhZXMoeCA9IEVVTklTYV8xX2Rlc2NyLCB5ID0gTkRWSV9tYXgpKSArDQogIGdlb21fZmxhdF92aW9saW4ocG9zaXRpb24gPSBwb3NpdGlvbl9udWRnZSh4ID0gMC4yLCB5ID0gMCksIGFscGhhID0gMC44LA0KICAgICAgICAgICAgICAgICAgIGZpbGwgPSAibGlnaHRibHVlIikgKw0KICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IGNhdGVnb3J5X05EVklfbWF4KSwNCiAgICAgICAgICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX2ppdHRlcih3aWR0aCA9IDAuMTUpLCBzaXplID0gMSwgYWxwaGEgPSAwLjI1KSArDQogIGdlb21fYm94cGxvdCh3aWR0aCA9IDAuMiwgb3V0bGllci5zaGFwZSA9IE5BLCBhbHBoYSA9IDAuNSkgKw0KICBzdGF0X3N1bW1hcnkoZnVuLnkgPSBtZWFuLCBnZW9tID0gInBvaW50Iiwgc2hhcGUgPSAyMCwgc2l6ZSA9IDEpICsNCiAgc3RhdF9zdW1tYXJ5KGZ1bi5kYXRhID0gZnVuY3Rpb24oeCkgZGF0YS5mcmFtZSh5ID0gbWF4KHgpICsgMC4xLCBsYWJlbCA9IGxlbmd0aCh4KSksDQogICAgICAgICAgICAgICBnZW9tID0gInRleHQiLCBhZXMobGFiZWwgPSAuLmxhYmVsLi4pLCB2anVzdCA9IDAuNSkgKw0KICBsYWJzKHkgPSAiTkRWSSBtYXgiLCB4ID0gIkVVTklTIGxldmVsIDEiKSArDQogIGd1aWRlcyhmaWxsID0gRkFMU0UsIGNvbG9yID0gRkFMU0UpICsNCiAgc2NhbGVfeF9kaXNjcmV0ZShsYWJlbHMgPSBmdW5jdGlvbih4KSBzdHJfd3JhcCh4LCB3aWR0aCA9IDE1KSkgKw0KICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiYmVsb3dfMjB0aCIgPSAiZ3JleSIsICJhYm92ZV8yMHRoIiA9ICJsaWdodGJsdWUiKSkgKw0KICB0aGVtZV9idygpICsgY29vcmRfZmxpcCgpDQoNCmdncGxvdChkYXRhID0gYWxsX0dQU192YWxpZCwNCiAgICAgICBhZXMoeCA9IEVVTklTYV8xX2Rlc2NyLCB5ID0gTkRNSV9taW4pKSArDQogIGdlb21fZmxhdF92aW9saW4ocG9zaXRpb24gPSBwb3NpdGlvbl9udWRnZSh4ID0gMC4yLCB5ID0gMCksIGFscGhhID0gMC44LA0KICAgICAgICAgICAgICAgICAgIGZpbGwgPSAibGlnaHRibHVlIikgKw0KICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IGNhdGVnb3J5X05ETUlfbWluKSwNCiAgICAgICAgICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX2ppdHRlcih3aWR0aCA9IDAuMTUpLCBzaXplID0gMSwgYWxwaGEgPSAwLjI1KSArDQogIGdlb21fYm94cGxvdCh3aWR0aCA9IDAuMiwgb3V0bGllci5zaGFwZSA9IE5BLCBhbHBoYSA9IDAuNSkgKw0KICBzdGF0X3N1bW1hcnkoZnVuLnkgPSBtZWFuLCBnZW9tID0gInBvaW50Iiwgc2hhcGUgPSAyMCwgc2l6ZSA9IDEpICsNCiAgc3RhdF9zdW1tYXJ5KGZ1bi5kYXRhID0gZnVuY3Rpb24oeCkgZGF0YS5mcmFtZSh5ID0gbWF4KHgpICsgMC4xLCBsYWJlbCA9IGxlbmd0aCh4KSksDQogICAgICAgICAgICAgICBnZW9tID0gInRleHQiLCBhZXMobGFiZWwgPSAuLmxhYmVsLi4pLCB2anVzdCA9IDAuNSkgKw0KICBsYWJzKHkgPSAiTkRNSSBtaW4iLCB4ID0gIkVVTklTIGxldmVsIDEiKSArDQogIGd1aWRlcyhmaWxsID0gRkFMU0UsIGNvbG9yID0gRkFMU0UpICsNCiAgc2NhbGVfeF9kaXNjcmV0ZShsYWJlbHMgPSBmdW5jdGlvbih4KSBzdHJfd3JhcCh4LCB3aWR0aCA9IDE1KSkgKw0KICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiYmVsb3dfMjB0aCIgPSAiZ3JleSIsICJhYm92ZV8yMHRoIiA9ICJsaWdodGJsdWUiKSkgKw0KICB0aGVtZV9idygpICsgY29vcmRfZmxpcCgpDQpgYGANCg0KIyBSRiBtb2RlbHMNCg0KVXNpbmcgdGhlIGNvbmRpdGlvbmFsIGluZmVyZW5jZSB2ZXJzaW9uIG9mIHJhbmRvbSBmb3Jlc3QgKGNmb3Jlc3QgaW4gcGFja2FnZSBwYXJ0eSkuIFN1Z2dlc3RlZCBpZiB0aGUgZGF0YSBhcmUgaGlnaGx5IGNvcnJlbGF0ZWQuIENmb3Jlc3QgaXMgbW9yZSBzdGFibGUgaW4gZGVyaXZpbmcgdmFyaWFibGUgaW1wb3J0YW5jZSB2YWx1ZXMgaW4gdGhlIHByZXNlbmNlIG9mIGhpZ2hseSBjb3JyZWxhdGVkIHZhcmlhYmxlcywgdGh1cyBwcm92aWRpbmcgYmV0dGVyIGFjY3VyYWN5IGluIGNhbGN1bGF0aW5nIHZhcmlhYmxlIGltcG9ydGFuY2UgKHJlZiBiZWxvdykuDQoNCkhvdGhvcm4sIFQuLCBIb3JuaWssIEsuIGFuZCBaZWlsZWlzLCBBLiAoMjAwNikgVW5iaWFzZWQgUmVjdXJzaXZlIFBvcnRpb25pbmc6IEEgQ29uZGl0aW9uYWwgSW5mZXJlbmNlIEZyYW1ld29yay4gSm91cm5hbCBvZiBDb21wdXRhdGlvbmFsIGFuZCBHcmFwaGljYWwgU3RhdGlzdGljcywgMTUsIDY1MS0NCjY3NC4gaHR0cDovL2R4LmRvaS5vcmcvMTAuMTE5OC8xMDYxODYwMDZYMTMzOTMzDQoNCiMjIEFsbCBHUFMgcG9pbnRzIGFib3ZlIHAyMA0KDQpGaWx0ZXIgdGhlIGRhdGEgdG8gZ2V0IG9ubHkgR1BTLXBvaW50cyBhYm92ZSBwMjAgb2YgTkRWSV9tYXggYW5kIE5ETUlfbWluLg0KDQpgYGB7cn0NCmFsbF9HUFNfdmFsaWQgPC0gYWxsX0dQU192YWxpZCAlPiUNCiAgc2VsZWN0KC1wZXJjZW50aWxlXzIwX05EVklfbWF4LCAtcGVyY2VudGlsZV8yMF9ORE1JX21pbikNCmBgYA0KDQpgYGB7cn0NCnBlcmNlbnRpbGVzIDwtIGFsbF9HUFNfdmFsaWQgJT4lDQogIGdyb3VwX2J5KEVVTklTYV8xKSAlPiUNCiAgc3VtbWFyaXplKA0KICAgIHBlcmNlbnRpbGVfMjBfTkRWSV9tYXggPSBxdWFudGlsZShORFZJX21heCwgMC4yMCwgbmEucm0gPSBUKSwNCiAgICBwZXJjZW50aWxlXzIwX05ETUlfbWluID0gcXVhbnRpbGUoTkRNSV9taW4sIDAuMjAsIG5hLnJtID0gVCksDQogICAgcGVyY2VudGlsZV84MF9ORFZJX21heCA9IHF1YW50aWxlKE5EVklfbWF4LCAwLjgwLCBuYS5ybSA9IFQpLA0KICAgIHBlcmNlbnRpbGVfODBfTkRNSV9taW4gPSBxdWFudGlsZShORE1JX21pbiwgMC44MCwgbmEucm0gPSBUKQ0KICAgICkNCg0KIyBKb2luIHRoZSBwZXJjZW50aWxlcyBiYWNrIHRvIHRoZSBvcmlnaW5hbCBkYXRhDQphbGxfR1BTX3ZhbGlkIDwtIGFsbF9HUFNfdmFsaWQgJT4lDQogIGxlZnRfam9pbihwZXJjZW50aWxlcywgYnkgPSAiRVVOSVNhXzEiKQ0KDQojIEZpbHRlciByb3dzIGFib3ZlIHRoZSAyMHRoIHBlcmNlbnRpbGUgZm9yIGJvdGggdmFyaWFibGVzIGZvciBlYWNoIGNhdGVnb3J5IG9mIEVVTklTYV8xDQpmaWx0ZXJlZF9kYXRhMSA8LSBhbGxfR1BTX3ZhbGlkICU+JQ0KICBmaWx0ZXIoDQogICAgTkRWSV9tYXggPj0gcGVyY2VudGlsZV8yMF9ORFZJX21heCAmIE5ETUlfbWluID49IHBlcmNlbnRpbGVfMjBfTkRNSV9taW4NCiAgICApICU+JQ0KICBmaWx0ZXIoIWlzLm5hKE5EVklfbWF4KSAmICFpcy5uYShORE1JX21heCkgJiAhaXMubmEoTkRXSV9tYXgpICYNCiAgICAgICAgICAgIWlzLm5hKFNBVklfbWF4KSAmICFpcy5uYShFVklfbWF4KSAmICFpcy5uYShORFZJX21pbikgJg0KICAgICAgICAgICAhaXMubmEoTkRNSV9taW4pICYgIWlzLm5hKE5EV0lfbWluKSAmICFpcy5uYShTQVZJX21pbikgJg0KICAgICAgICAgICAhaXMubmEoRVZJX21pbikpICU+JQ0KICBtdXRhdGUoRVVOSVNhXzEgPSBhcy5mYWN0b3IoRVVOSVNhXzEpKSAlPiUNCiAgZmlsdGVyKEVWSV9tYXggPD0gMSAmIEVWSV9taW4gPj0gLTEpDQpgYGANCg0KU3BsaXQgaW50byB0cmFpbmluZyBhbmQgdGVzdCBkYXRhIHNldHMuDQoNCmBgYHtyfQ0KdHJhaW5faW5kaWNlczEgPC0gc2FtcGxlKDE6bnJvdyhmaWx0ZXJlZF9kYXRhMSksIDAuNyAqIG5yb3coZmlsdGVyZWRfZGF0YTEpKQ0KdHJhaW5fZGF0YTEgPC0gZmlsdGVyZWRfZGF0YTFbdHJhaW5faW5kaWNlczEsIF0NCnRlc3RfZGF0YTEgPC0gZmlsdGVyZWRfZGF0YTFbLXRyYWluX2luZGljZXMxLCBdDQpgYGANCg0KTnVtYmVyIG9mIHBvaW50cyBwZXIgY2F0ZWdvcnkgZm9yIGZpbHRlcmVkIGRhdGE6DQoNCmBgYHtyfQ0KZmlsdGVyZWRfZGF0YTEgJT4lIGNvdW50KEVVTklTYV8xKQ0KYGBgDQoNCkludmVzdGlnYXRlIHBhY2thZ2UgZ2dwYXJ0eSAoZS5nLiBhdXRvcGxvdCBmdW5jdGlvbiwgYW5kIG1vcmUpLg0KDQpUTy1ETzogDQpDaG9vc2UgdGhlIGh5cGVycGFyYW1ldGVyIG10cnkgYmFzZWQgb24gdGhlIHNxdWFyZSByb290IG9mIHRoZSBudW1iZXIgb2YgcHJlZGljdG9yIHZhcmlhYmxlcyAoSGFzdGllIGV0IGFsLiwgMjAwOSktDQoNCkhhc3RpZSwgVC4sIFRpYnNoaXJhbmksIFIuLCAmIEZyaWVkbWFuLCBKLiAoMjAwOSkuIFRoZSBlbGVtZW50cyBvZiBzdGF0aXN0aWNhbA0KbGVhcm5pbmc6IERhdGEgbWluaW5nLCBpbmZlcmVuY2UsIGFuZCBwcmVkaWN0aW9uLiBTcHJpbmdlciBTY2llbmNlICYNCkJ1c2luZXNzIE1lZGlhLg0KDQpNYXliZSBUT19ETzoNCldlIHZhcmlhdGVkIG50cmVlIGZyb20gNTAgdG8gODAwIGluIHN0ZXBzIG9mIDUwLCBsZWF2aW5nIG10cnkgY29uc3RhbnQgYXQgMi4gVGlzIHBhcmFtZXRlciB2YXJpYXRpb24gc2hvd2VkIHRoYXQgbnRyZWU9NTAwIHdhcyBvcHRpbWFsLCB3aGlsZSBoaWdoZXIgbnRyZWUgbGVkIHRvIG5vIGZ1cnRoZXIgbW9kZWwgaW1wcm92ZW1lbnQgKFN1cHBsZW1lbnRhcnkgRmlnLiBTMTApLiBTdWJzZXF1ZW50bHksIHRoZSBoeXBlcnBhcmFtZXRlciBtdHJ5IHdhcyB2YXJpZWQgZnJvbSAyIHRvIDggd2l0aCBjb25zdGFudCBudHJlZT01MDAuIEhlcmUsIG10cnk9MyBsZWQgdG8gdGhlIGJlc3QgcmVzdWx0cyBpbiBhbG1vc3QgYWxsIGNhc2VzIChTdXBwbGVtZW50YXJ5IEZpZy4gUzExKS4gQ29uc2VxdWVudGx5LCB3ZSBjaG9zZSBudHJlZT01MDAgYW5kIG10cnk9MyBmb3Igb3VyIG1haW4gYW5hbHlzaXMgYWNyb3NzIGFsbCBzdHVkeSBzaXRlcy4NCg0KYGBge3J9DQpyZl9jZm9yZXN0MSA8LSBwYXJ0eTo6Y2ZvcmVzdChFVU5JU2FfMSB+IE5EVklfbWF4ICsgTkRWSV9taW4gKyBORE1JX21heCArDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE5ETUlfbWluICsgTkRXSV9tYXggKyBORFdJX21pbiArIEVWSV9tYXggKw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBFVklfbWluICsgU0FWSV9tYXggKyBTQVZJX21pbiArIGNhbm9weV9oZWlnaHQsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IHRyYWluX2RhdGExLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29udHJvbHMgPSBjZm9yZXN0X2NvbnRyb2woDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG10cnkgPSAzLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIG10cnkgPSBzcXJ0KDExKQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIERlZmF1bHQgbXRyeSA9IDUNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBCYWdnaW5nOiBtdHJ5ID0gTlVMTA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIG9yID0gbnVtYmVyIG9mIGlucHV0IHZhcmlhYmxlcw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBudHJlZSA9IDUwMCkgIyBEZWZhdWx0LCB0cnkgaW5jcmVhc2luZw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSANCmBgYA0KDQpgYGB7cn0NCnByZWRpY3Rpb25zX3JmX2Nmb3Jlc3QxIDwtIHByZWRpY3QocmZfY2ZvcmVzdDEsIG5ld2RhdGEgPSB0ZXN0X2RhdGExLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBPT0IgPSBUUlVFLCB0eXBlID0gInJlc3BvbnNlIikNCmBgYA0KDQpDb25mdXNpb24gbWF0cml4Og0KDQpgYGB7cn0NCmNvbmZ1c2lvbk1hdHJpeChwcmVkaWN0aW9uc19yZl9jZm9yZXN0MSwgdGVzdF9kYXRhMSRFVU5JU2FfMSkNCmBgYA0KDQpTdXJyb2dhdGVUcmVlIC0tPiBkb2VzIG5vdCB3b3JrDQoNCmBgYHtyfQ0KdmFyaW1wX3JmX2Nmb3Jlc3QxIDwtIHBhcnR5Ojp2YXJpbXAocmZfY2ZvcmVzdDEsIGNvbmRpdGlvbmFsID0gRikgDQojIGNvbmRpdGlvbmFsID0gVCBhZGp1c3RzIGZvciBjb3JyZWxhdGlvbnMgYmV0d2VlbiBwcmVkaWN0b3IgdmFyaWFibGVzDQojIFRha2VzIGxvbmchDQpgYGANCg0KYGBge3J9DQojIHBhcnR5Ojp2YXJpbXAocmZfY2ZvcmVzdDEsIGNvbmRpdGlvbmFsID0gVCkgDQojIGNvbmRpdGlvbmFsID0gVCBhZGp1c3RzIGZvciBjb3JyZWxhdGlvbnMgYmV0d2VlbiBwcmVkaWN0b3IgdmFyaWFibGVzDQojIFRha2VzIGxvbmchDQpgYGANCg0KVmFyaWFibGUgSW1wb3J0YW5jZSBQbG90DQoNCmBgYHtyfQ0KdmFyaW1wX3JmX2Nmb3Jlc3QxX2RmIDwtIGRhdGEuZnJhbWUoVmFyaWFibGUgPSBuYW1lcyh2YXJpbXBfcmZfY2ZvcmVzdDEpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSW1wb3J0YW5jZSA9IHZhcmltcF9yZl9jZm9yZXN0MSkNCmdncGxvdCh2YXJpbXBfcmZfY2ZvcmVzdDFfZGYsDQogICAgICAgYWVzKHggPSByZW9yZGVyKFZhcmlhYmxlLCBJbXBvcnRhbmNlKSwgeSA9IEltcG9ydGFuY2UpKSArDQogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBmaWxsID0gImxpZ2h0Ymx1ZSIpICsNCiAgY29vcmRfZmxpcCgpICsgdGhlbWVfbWluaW1hbCgpICsNCiAgbGFicyh0aXRsZSA9ICJWYXJpYWJsZSBJbXBvcnRhbmNlIiwgeCA9ICJWYXJpYWJsZXMiLCB5ID0gIkltcG9ydGFuY2UiKQ0KYGBgDQoNClRyZWUgVmlzdWFsaXphdGlvbg0KDQpgYGB7cn0NCiMgQ3JlYXRlIGEgc2luZ2xlIGNvbmRpdGlvbmFsIGluZmVyZW5jZSB0cmVlIHVzaW5nIGN0cmVlDQpzaW5nbGVfdHJlZTEgPC0gY3RyZWUoRVVOSVNhXzEgfiBORFZJX21heCArIE5EVklfbWluICsgTkRNSV9tYXggKyBORE1JX21pbiArDQogICAgICAgICAgICAgICAgICAgICAgIE5EV0lfbWF4ICsgTkRXSV9taW4gKyBFVklfbWF4ICsgRVZJX21pbiArIFNBVklfbWF4ICsNCiAgICAgICAgICAgICAgICAgICAgICAgU0FWSV9taW4gKyBjYW5vcHlfaGVpZ2h0LA0KICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IHRyYWluX2RhdGExKQ0KDQojIFBsb3QgdGhlIHNpbmdsZSB0cmVlIHVzaW5nDQphdXRvcGxvdChzaW5nbGVfdHJlZTEpDQpgYGANCg0KIyMgQWxsIEdQUyBwb2ludHMgd2l0aGluIElRIHJhbmdlDQoNCkZpbHRlciB0aGUgZGF0YSB0byBnZXQgb25seSBHUFMtcG9pbnRzIHdpdGhpbiBJUSByYW5nZSBvZiBORFZJX21heCBhbmQgTkRNSV9taW4uDQoNCmBgYHtyfQ0KSVFfcmFuZ2VzIDwtIGFsbF9HUFNfdmFsaWQgJT4lDQogIGdyb3VwX2J5KEVVTklTYV8xKSAlPiUNCiAgc3VtbWFyaXplKA0KICAgIFExX05EVklfbWF4ID0gcXVhbnRpbGUoTkRWSV9tYXgsIDAuMjUsIG5hLnJtID0gVCksDQogICAgUTFfTkRNSV9taW4gPSBxdWFudGlsZShORE1JX21pbiwgMC4yNSwgbmEucm0gPSBUKSwNCiAgICBRM19ORFZJX21heCA9IHF1YW50aWxlKE5EVklfbWF4LCAwLjc1LCBuYS5ybSA9IFQpLA0KICAgIFEzX05ETUlfbWluID0gcXVhbnRpbGUoTkRNSV9taW4sIDAuNzUsIG5hLnJtID0gVCkNCiAgICApDQoNCiMgSm9pbiB0aGUgSVEgcmFuZ2VzIGJhY2sgdG8gdGhlIG9yaWdpbmFsIGRhdGENCmFsbF9HUFNfdmFsaWQgPC0gYWxsX0dQU192YWxpZCAlPiUNCiAgbGVmdF9qb2luKElRX3JhbmdlcywgYnkgPSAiRVVOSVNhXzEiKQ0KDQojIEZpbHRlciByb3dzIHdpdGhpbiB0aGUgSVFSIHJhbmdlIGZvciBib3RoIHZhcmlhYmxlcw0KZmlsdGVyZWRfZGF0YTIgPC0gYWxsX0dQU192YWxpZCAlPiUNCiAgZmlsdGVyKA0KICAgIChORFZJX21heCA+PSBRMV9ORFZJX21heCAmIE5EVklfbWF4IDw9IFEzX05EVklfbWF4KSAmDQogICAgKE5ETUlfbWluID49IFExX05ETUlfbWluICYgTkRNSV9taW4gPD0gUTNfTkRNSV9taW4pDQogICAgKSAlPiUNCiAgZmlsdGVyKCFpcy5uYShORFZJX21heCkgJiAhaXMubmEoTkRNSV9tYXgpICYgIWlzLm5hKE5EV0lfbWF4KSAmDQogICAgICAgICAgICFpcy5uYShTQVZJX21heCkgJiAhaXMubmEoRVZJX21heCkgJiAhaXMubmEoTkRWSV9taW4pICYNCiAgICAgICAgICAgIWlzLm5hKE5ETUlfbWluKSAmICFpcy5uYShORFdJX21pbikgJiAhaXMubmEoU0FWSV9taW4pICYNCiAgICAgICAgICAgIWlzLm5hKEVWSV9taW4pKSAlPiUNCiAgbXV0YXRlKEVVTklTYV8xID0gYXMuZmFjdG9yKEVVTklTYV8xKSkgJT4lDQogIGZpbHRlcihFVklfbWF4IDw9IDEgJiBFVklfbWluID49IC0xKQ0KYGBgDQoNClNwbGl0IGludG8gdHJhaW5pbmcgYW5kIHRlc3QgZGF0YSBzZXRzLg0KDQpgYGB7cn0NCnRyYWluX2luZGljZXMyIDwtIHNhbXBsZSgxOm5yb3coZmlsdGVyZWRfZGF0YTIpLCAwLjcgKiBucm93KGZpbHRlcmVkX2RhdGEyKSkNCnRyYWluX2RhdGEyIDwtIGZpbHRlcmVkX2RhdGEyW3RyYWluX2luZGljZXMyLCBdDQp0ZXN0X2RhdGEyIDwtIGZpbHRlcmVkX2RhdGEyWy10cmFpbl9pbmRpY2VzMiwgXQ0KYGBgDQoNCk51bWJlciBvZiBwb2ludHMgcGVyIGNhdGVnb3J5IGZvciBmaWx0ZXJlZCBkYXRhOg0KDQpgYGB7cn0NCmZpbHRlcmVkX2RhdGEyICU+JSBjb3VudChFVU5JU2FfMSkNCmBgYA0KDQpgYGB7cn0NCnJmX2Nmb3Jlc3QyIDwtIHBhcnR5OjpjZm9yZXN0KEVVTklTYV8xIH4gTkRWSV9tYXggKyBORFZJX21pbiArIE5ETUlfbWF4ICsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTkRNSV9taW4gKyBORFdJX21heCArIE5EV0lfbWluICsgRVZJX21heCArDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEVWSV9taW4gKyBTQVZJX21heCArIFNBVklfbWluICsgY2Fub3B5X2hlaWdodCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhID0gdHJhaW5fZGF0YTIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb250cm9scyA9IGNmb3Jlc3RfY29udHJvbCgNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbXRyeSA9IDMsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgbXRyeSA9IHNxcnQoMTEpDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgRGVmYXVsdCBtdHJ5ID0gNQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIEJhZ2dpbmc6IG10cnkgPSBOVUxMDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgb3IgPSBudW1iZXIgb2YgaW5wdXQgdmFyaWFibGVzDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG50cmVlID0gNTAwKSAjIERlZmF1bHQsIHRyeSBpbmNyZWFzaW5nDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICApIA0KYGBgDQoNCmBgYHtyfQ0KcHJlZGljdGlvbnNfcmZfY2ZvcmVzdDIgPC0gcHJlZGljdChyZl9jZm9yZXN0MiwgbmV3ZGF0YSA9IHRlc3RfZGF0YTIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE9PQiA9IFRSVUUsIHR5cGUgPSAicmVzcG9uc2UiKQ0KYGBgDQoNCkNvbmZ1c2lvbiBtYXRyaXg6DQoNCmBgYHtyfQ0KY29uZnVzaW9uTWF0cml4KHByZWRpY3Rpb25zX3JmX2Nmb3Jlc3QyLCB0ZXN0X2RhdGEyJEVVTklTYV8xKQ0KYGBgDQoNCmBgYHtyfQ0KdmFyaW1wX3JmX2Nmb3Jlc3QyIDwtIHBhcnR5Ojp2YXJpbXAocmZfY2ZvcmVzdDIsIGNvbmRpdGlvbmFsID0gRikgDQpgYGANCg0KVmFyaWFibGUgSW1wb3J0YW5jZSBQbG90DQoNCmBgYHtyfQ0KdmFyaW1wX3JmX2Nmb3Jlc3QyX2RmIDwtIGRhdGEuZnJhbWUoVmFyaWFibGUgPSBuYW1lcyh2YXJpbXBfcmZfY2ZvcmVzdDIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSW1wb3J0YW5jZSA9IHZhcmltcF9yZl9jZm9yZXN0MikNCmdncGxvdCh2YXJpbXBfcmZfY2ZvcmVzdDJfZGYsDQogICAgICAgYWVzKHggPSByZW9yZGVyKFZhcmlhYmxlLCBJbXBvcnRhbmNlKSwgeSA9IEltcG9ydGFuY2UpKSArDQogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBmaWxsID0gImxpZ2h0Ymx1ZSIpICsNCiAgY29vcmRfZmxpcCgpICsgdGhlbWVfbWluaW1hbCgpICsNCiAgbGFicyh0aXRsZSA9ICJWYXJpYWJsZSBJbXBvcnRhbmNlIiwgeCA9ICJWYXJpYWJsZXMiLCB5ID0gIkltcG9ydGFuY2UiKQ0KYGBgDQoNCiMjIEFsbCBHUFMgcG9pbnRzIHdpdGhpbiBtZWFuICsvLSBTRA0KDQpGaWx0ZXIgdGhlIGRhdGEgdG8gZ2V0IG9ubHkgR1BTLXBvaW50cyB3aXRoaW4gbWVhbiArLy0gU0Qgb2YgTkRWSV9tYXggYW5kIE5ETUlfbWluLg0KDQpgYGB7cn0NCm1lYW5fc2QgPC0gYWxsX0dQU192YWxpZCAlPiUNCiAgZ3JvdXBfYnkoRVVOSVNhXzEpICU+JQ0KICBzdW1tYXJpemUoDQogICAgbWVhbl9ORFZJX21heCA9IG1lYW4oYWxsX0dQU192YWxpZCRORFZJX21heCwgbmEucm0gPSBUKSwNCiAgICBtZWFuX05ETUlfbWluID0gbWVhbihhbGxfR1BTX3ZhbGlkJE5ETUlfbWluLCBuYS5ybSA9IFQpLA0KICAgIHNkX05EVklfbWF4ID0gc2QoYWxsX0dQU192YWxpZCRORFZJX21heCwgbmEucm0gPSBUKSwNCiAgICBzZF9ORE1JX21pbiA9IHNkKGFsbF9HUFNfdmFsaWQkTkRNSV9taW4sIG5hLnJtID0gVCkNCiAgICApDQoNCiMgSm9pbiB0aGUgSVEgcmFuZ2VzIGJhY2sgdG8gdGhlIG9yaWdpbmFsIGRhdGENCmFsbF9HUFNfdmFsaWQgPC0gYWxsX0dQU192YWxpZCAlPiUNCiAgbGVmdF9qb2luKG1lYW5fc2QsIGJ5ID0gIkVVTklTYV8xIikNCg0KIyBGaWx0ZXIgcm93cyB3aXRoaW4gdGhlIHNwZWNpZmllZCByYW5nZSBmb3IgYm90aCB2YXJpYWJsZXMNCmZpbHRlcmVkX2RhdGEzIDwtIGFsbF9HUFNfdmFsaWQgJT4lDQogIGZpbHRlcigNCiAgICAoTkRWSV9tYXggPj0gKG1lYW5fTkRWSV9tYXggLSBzZF9ORFZJX21heCkgJiBORFZJX21heCA8PSAobWVhbl9ORFZJX21heCArIHNkX05EVklfbWF4KSkgJg0KICAgICAgKE5ETUlfbWluID49IChtZWFuX05ETUlfbWluIC0gc2RfTkRNSV9taW4pICYgTkRNSV9taW4gPD0gKG1lYW5fTkRNSV9taW4gKyBzZF9ORE1JX21pbikpDQogICAgKSAlPiUNCiAgZmlsdGVyKCFpcy5uYShORFZJX21heCkgJiAhaXMubmEoTkRNSV9tYXgpICYgIWlzLm5hKE5EV0lfbWF4KSAmDQogICAgICAgICAgICFpcy5uYShTQVZJX21heCkgJiAhaXMubmEoRVZJX21heCkgJiAhaXMubmEoTkRWSV9taW4pICYNCiAgICAgICAgICAgIWlzLm5hKE5ETUlfbWluKSAmICFpcy5uYShORFdJX21pbikgJiAhaXMubmEoU0FWSV9taW4pICYNCiAgICAgICAgICAgIWlzLm5hKEVWSV9taW4pKSAlPiUNCiAgbXV0YXRlKEVVTklTYV8xID0gYXMuZmFjdG9yKEVVTklTYV8xKSkgJT4lDQogIGZpbHRlcihFVklfbWF4IDw9IDEgJiBFVklfbWluID49IC0xKQ0KYGBgDQoNClNwbGl0IGludG8gdHJhaW5pbmcgYW5kIHRlc3QgZGF0YSBzZXRzLg0KDQpgYGB7cn0NCnRyYWluX2luZGljZXMzIDwtIHNhbXBsZSgxOm5yb3coZmlsdGVyZWRfZGF0YTMpLCAwLjcgKiBucm93KGZpbHRlcmVkX2RhdGEzKSkNCnRyYWluX2RhdGEzIDwtIGZpbHRlcmVkX2RhdGEzW3RyYWluX2luZGljZXMzLCBdDQp0ZXN0X2RhdGEzIDwtIGZpbHRlcmVkX2RhdGEzWy10cmFpbl9pbmRpY2VzMywgXQ0KYGBgDQoNCk51bWJlciBvZiBwb2ludHMgcGVyIGNhdGVnb3J5IGZvciBmaWx0ZXJlZCBkYXRhOg0KDQpgYGB7cn0NCmZpbHRlcmVkX2RhdGEzICU+JSBjb3VudChFVU5JU2FfMSkNCmBgYA0KDQpgYGB7cn0NCnJmX2Nmb3Jlc3QzIDwtIHBhcnR5OjpjZm9yZXN0KEVVTklTYV8xIH4gTkRWSV9tYXggKyBORFZJX21pbiArIE5ETUlfbWF4ICsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTkRNSV9taW4gKyBORFdJX21heCArIE5EV0lfbWluICsgRVZJX21heCArDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEVWSV9taW4gKyBTQVZJX21heCArIFNBVklfbWluICsgY2Fub3B5X2hlaWdodCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhID0gdHJhaW5fZGF0YTMsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb250cm9scyA9IGNmb3Jlc3RfY29udHJvbCgNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbXRyeSA9IDMsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgbXRyeSA9IHNxcnQoMTEpDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgRGVmYXVsdCBtdHJ5ID0gNQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIEJhZ2dpbmc6IG10cnkgPSBOVUxMDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgb3IgPSBudW1iZXIgb2YgaW5wdXQgdmFyaWFibGVzDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG50cmVlID0gNTAwKSAjIERlZmF1bHQsIHRyeSBpbmNyZWFzaW5nDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICApIA0KYGBgDQoNCmBgYHtyfQ0KcHJlZGljdGlvbnNfcmZfY2ZvcmVzdDMgPC0gcHJlZGljdChyZl9jZm9yZXN0MywgbmV3ZGF0YSA9IHRlc3RfZGF0YTMsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE9PQiA9IFRSVUUsIHR5cGUgPSAicmVzcG9uc2UiKQ0KYGBgDQoNCkNvbmZ1c2lvbiBtYXRyaXg6DQoNCmBgYHtyfQ0KY29uZnVzaW9uTWF0cml4KHByZWRpY3Rpb25zX3JmX2Nmb3Jlc3QzLCB0ZXN0X2RhdGEzJEVVTklTYV8xKQ0KYGBgDQoNCmBgYHtyfQ0KdmFyaW1wX3JmX2Nmb3Jlc3QzIDwtIHBhcnR5Ojp2YXJpbXAocmZfY2ZvcmVzdDMsIGNvbmRpdGlvbmFsID0gRikgDQpgYGANCg0KVmFyaWFibGUgSW1wb3J0YW5jZSBQbG90DQoNCmBgYHtyfQ0KdmFyaW1wX3JmX2Nmb3Jlc3QzX2RmIDwtIGRhdGEuZnJhbWUoVmFyaWFibGUgPSBuYW1lcyh2YXJpbXBfcmZfY2ZvcmVzdDMpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSW1wb3J0YW5jZSA9IHZhcmltcF9yZl9jZm9yZXN0MykNCmdncGxvdCh2YXJpbXBfcmZfY2ZvcmVzdDNfZGYsDQogICAgICAgYWVzKHggPSByZW9yZGVyKFZhcmlhYmxlLCBJbXBvcnRhbmNlKSwgeSA9IEltcG9ydGFuY2UpKSArDQogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBmaWxsID0gImxpZ2h0Ymx1ZSIpICsNCiAgY29vcmRfZmxpcCgpICsgdGhlbWVfbWluaW1hbCgpICsNCiAgbGFicyh0aXRsZSA9ICJWYXJpYWJsZSBJbXBvcnRhbmNlIiwgeCA9ICJWYXJpYWJsZXMiLCB5ID0gIkltcG9ydGFuY2UiKQ0KYGBgDQoNCiMjIEFsbCBHUFMgcG9pbnRzIGFib3ZlIHAyMCBhbmQgYmVsb3cgcDgwDQoNCkZpbHRlciB0aGUgZGF0YSB0byBnZXQgb25seSBHUFMtcG9pbnRzIGFib3ZlIHAyMCBhbmQgYmVsb3cgcDgwIG9mIE5EVklfbWF4IGFuZCBORE1JX21pbi4NCg0KYGBge3J9DQojIEZpbHRlciByb3dzIGFib3ZlIHRoZSAyMHRoIHBlcmNlbnRpbGUgYW5kIGJlbG93IHRoZSA4MHRoIHBlcmNlbnRpbGUgZm9yIGJvdGggdmFyaWFibGVzDQpmaWx0ZXJlZF9kYXRhNCA8LSBhbGxfR1BTX3ZhbGlkICU+JQ0KICBmaWx0ZXIoDQogICAgKE5EVklfbWF4ID49IHBlcmNlbnRpbGVfMjBfTkRWSV9tYXggJiBORFZJX21heCA8PSBwZXJjZW50aWxlXzgwX05EVklfbWF4KSAmDQogICAgKE5ETUlfbWluID49IHBlcmNlbnRpbGVfMjBfTkRNSV9taW4gJiBORE1JX21pbiA8PSBwZXJjZW50aWxlXzgwX05ETUlfbWluKQ0KICAgICkgJT4lDQogIGZpbHRlcighaXMubmEoTkRWSV9tYXgpICYgIWlzLm5hKE5ETUlfbWF4KSAmICFpcy5uYShORFdJX21heCkgJg0KICAgICAgICAgICAhaXMubmEoU0FWSV9tYXgpICYgIWlzLm5hKEVWSV9tYXgpICYgIWlzLm5hKE5EVklfbWluKSAmDQogICAgICAgICAgICFpcy5uYShORE1JX21pbikgJiAhaXMubmEoTkRXSV9taW4pICYgIWlzLm5hKFNBVklfbWluKSAmDQogICAgICAgICAgICFpcy5uYShFVklfbWluKSkgJT4lDQogIG11dGF0ZShFVU5JU2FfMSA9IGFzLmZhY3RvcihFVU5JU2FfMSkpICU+JQ0KICBmaWx0ZXIoRVZJX21heCA8PSAxICYgRVZJX21pbiA+PSAtMSkNCmBgYA0KDQpTcGxpdCBpbnRvIHRyYWluaW5nIGFuZCB0ZXN0IGRhdGEgc2V0cy4NCg0KYGBge3J9DQp0cmFpbl9pbmRpY2VzNCA8LSBzYW1wbGUoMTpucm93KGZpbHRlcmVkX2RhdGE0KSwgMC43ICogbnJvdyhmaWx0ZXJlZF9kYXRhNCkpDQp0cmFpbl9kYXRhNCA8LSBmaWx0ZXJlZF9kYXRhNFt0cmFpbl9pbmRpY2VzNCwgXQ0KdGVzdF9kYXRhNCA8LSBmaWx0ZXJlZF9kYXRhNFstdHJhaW5faW5kaWNlczQsIF0NCmBgYA0KDQpOdW1iZXIgb2YgcG9pbnRzIHBlciBjYXRlZ29yeSBmb3IgZmlsdGVyZWQgZGF0YToNCg0KYGBge3J9DQpmaWx0ZXJlZF9kYXRhNCAlPiUgY291bnQoRVVOSVNhXzEpDQpgYGANCg0KYGBge3J9DQpyZl9jZm9yZXN0NCA8LSBwYXJ0eTo6Y2ZvcmVzdChFVU5JU2FfMSB+IE5EVklfbWF4ICsgTkRWSV9taW4gKyBORE1JX21heCArDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE5ETUlfbWluICsgTkRXSV9tYXggKyBORFdJX21pbiArIEVWSV9tYXggKw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBFVklfbWluICsgU0FWSV9tYXggKyBTQVZJX21pbiArIGNhbm9weV9oZWlnaHQsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IHRyYWluX2RhdGE0LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29udHJvbHMgPSBjZm9yZXN0X2NvbnRyb2woDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG10cnkgPSAzLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIG10cnkgPSBzcXJ0KDExKQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIERlZmF1bHQgbXRyeSA9IDUNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBCYWdnaW5nOiBtdHJ5ID0gTlVMTA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIG9yID0gbnVtYmVyIG9mIGlucHV0IHZhcmlhYmxlcw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBudHJlZSA9IDUwMCkgIyBEZWZhdWx0LCB0cnkgaW5jcmVhc2luZw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSANCmBgYA0KDQpgYGB7cn0NCnByZWRpY3Rpb25zX3JmX2Nmb3Jlc3Q0IDwtIHByZWRpY3QocmZfY2ZvcmVzdDQsIG5ld2RhdGEgPSB0ZXN0X2RhdGE0LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBPT0IgPSBUUlVFLCB0eXBlID0gInJlc3BvbnNlIikNCmBgYA0KDQpDb25mdXNpb24gbWF0cml4Og0KDQpgYGB7cn0NCmNvbmZ1c2lvbk1hdHJpeChwcmVkaWN0aW9uc19yZl9jZm9yZXN0NCwgdGVzdF9kYXRhNCRFVU5JU2FfMSkNCmBgYA0KDQojIEhFUkU6IENvbXBhcmUgUkYgMS00DQoNCiMgQ29yZGlsbGVyYSBkYXRhDQoNCmBgYHtyfQ0KQWxwaW5lR3Jhc3NsYW5kc19pbmRpY2VzIDwtIHJlYWRfY3N2KA0KICAiQzovRGF0YS9NT1RJVkFURS9Db3JkaWxsZXJhL0FscGluZUdyYXNzbGFuZHMvQWxwaW5lR3Jhc3NsYW5kX1NlbnRpbmVsX1Bsb3RfQWxseWVhcl9BbGxtZXRyaWNzLmNzdiIpDQpBbHBpbmVHcmFzc2xhbmRzX3BoZW4gPC0gcmVhZF9jc3YoDQogICJDOi9EYXRhL01PVElWQVRFL0NvcmRpbGxlcmEvQWxwaW5lR3Jhc3NsYW5kcy9BbHBpbmVHcmFzc2xhbmRzX1BoZW5vbG9neV9TT1NfRU9TX1BlYWtfTkRWSV9BbXBsaXR1ZGUuY3N2IikNCkFscGluZUdyYXNzbGFuZHNfQ0ggPC0gcmVhZF9jc3YoDQogICJDOi9EYXRhL01PVElWQVRFL0NvcmRpbGxlcmEvQWxwaW5lR3Jhc3NsYW5kcy9BbHBpbmVHcmFzc2xhbmRzX0Nhbm9weUhlaWdodF8xbS5jc3YiKQ0KVmVnZXRhdGlvblR5cGVzX2luZGljZXMgPC0gcmVhZF9jc3YoDQogICJDOi9EYXRhL01PVElWQVRFL0NvcmRpbGxlcmEvVmVnZXRhdGlvblR5cGVzL1ZlZ2V0YXRpb25UeXBlc19TZW50aW5lbF9QbG90X0FsbFllYXJfQWxsbWV0cmljcy5jc3YiKQ0KVmVnZXRhdGlvblR5cGVzX3BoZW4gPC0gcmVhZF9jc3YoDQogICJDOi9EYXRhL01PVElWQVRFL0NvcmRpbGxlcmEvVmVnZXRhdGlvblR5cGVzL1ZlZ2V0YXRpb25UeXBlc19QaGVub2xvZ3lfU09TX0VPU19QZWFrX05EVklfQW1wbGl0dWRlLmNzdiIpDQpWZWdldGF0aW9uVHlwZXNfQ0ggPC0gcmVhZF9jc3YoDQogICJDOi9EYXRhL01PVElWQVRFL0NvcmRpbGxlcmEvVmVnZXRhdGlvblR5cGVzL1ZlZ2V0YXRpb25UeXBlc19DYW5vcHlIZWlnaHRfMW0uY3N2IikNCmBgYA0KDQpgYGB7cn0NCkFscGluZUdyYXNzbGFuZHMgPC0gQWxwaW5lR3Jhc3NsYW5kc19pbmRpY2VzICU+JQ0KICBzZWxlY3QoLWBzeXN0ZW06aW5kZXhgLCAtLmdlbywgLUxvY2FsaWRhZCkgJT4lDQogIHJlbmFtZShIw6FiaXRhdCA9ICJI77+9Yml0YXQiKSAlPiUgDQogIGZ1bGxfam9pbihBbHBpbmVHcmFzc2xhbmRzX3BoZW4gICU+JQ0KICAgICAgICAgICAgICBzZWxlY3QoLWBzeXN0ZW06aW5kZXhgLCAtLmdlbywgLUxvY2FsaWRhZCkgJT4lDQogICAgICAgICAgICAgIHJlbmFtZShIw6FiaXRhdCA9ICJI77+9Yml0YXQiKSkgJT4lDQogIGZ1bGxfam9pbihBbHBpbmVHcmFzc2xhbmRzX0NIICAlPiUNCiAgICAgICAgICAgICAgc2VsZWN0KC1gc3lzdGVtOmluZGV4YCwgLS5nZW8sIC1Mb2NhbGlkYWQpKSAlPiUNCiAgc2VsZWN0KC1EYXRlX195ZWFyLCAtIGBQcmVjaXNp77+9bmApICU+JQ0KICBtdXRhdGUoREFURSA9IHltZChEQVRFKSkgJT4lDQogIHJlbmFtZShJRCA9ICJSZWxldmVfbnVtIikgJT4lDQogIG11dGF0ZShJRCA9IGFzLmNoYXJhY3RlcihJRCkpICU+JQ0KICBtdXRhdGUobGF5ZXIgPSAiQWxwaW5lR3Jhc3NsYW5kcyIpDQpgYGANCg0KYGBge3J9DQpWZWdldGF0aW9uVHlwZXMgPC0gVmVnZXRhdGlvblR5cGVzX2luZGljZXMgJT4lDQogIHNlbGVjdCgtYHN5c3RlbTppbmRleGAsIC0uZ2VvKSAlPiUNCiAgZnVsbF9qb2luKFZlZ2V0YXRpb25UeXBlc19waGVuICAlPiUNCiAgICAgICAgICAgICAgc2VsZWN0KC1gc3lzdGVtOmluZGV4YCwgLS5nZW8pKSAlPiUNCiAgZnVsbF9qb2luKFZlZ2V0YXRpb25UeXBlc19DSCAgJT4lDQogICAgICAgICAgICAgIHNlbGVjdCgtYHN5c3RlbTppbmRleGAsIC0uZ2VvKSkgJT4lDQogIHJlbmFtZShIw6FiaXRhdCA9ICJUWVBFIikgJT4lDQogIG11dGF0ZShsYXllciA9ICJWZWdldGF0aW9uVHlwZXMiKQ0KYGBgDQoNCk1lcmdlIGJvdGggZGF0YXNldHM6DQoNCmBgYHtyfQ0KY29yZGlsbGVyYSA8LSBiaW5kX3Jvd3MoDQogIEFscGluZUdyYXNzbGFuZHMgJT4lIHNlbGVjdChEQVRFLCBJRCwgc3RhcnRzX3dpdGgoIk5ETUkiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0YXJ0c193aXRoKCJORFZJIiksIEjDoWJpdGF0LCAiRU9TX0RPWSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiUGVha19ET1kiLCAiU09TX0RPWSIsICJTZWFzb25fTGVuZ3RoIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJjYW5vcHlfaGVpZ2h0IiwgImxheWVyIiksDQogIFZlZ2V0YXRpb25UeXBlcyAlPiUgc2VsZWN0KERBVEUsIElELCBzdGFydHNfd2l0aCgiTkRNSSIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RhcnRzX3dpdGgoIk5EVkkiKSwgSMOhYml0YXQsICJFT1NfRE9ZIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJQZWFrX0RPWSIsICJTT1NfRE9ZIiwgIlNlYXNvbl9MZW5ndGgiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImNhbm9weV9oZWlnaHQiLCAibGF5ZXIiKQ0KICApICU+JQ0KICBtdXRhdGUoRVVOSVNhXzEgPSBjYXNlX3doZW4oDQogICAgSMOhYml0YXQgPSBzdHJfZGV0ZWN0KEjDoWJpdGF0LCAiUGFzdGl6YWx8Q2VydnVuYWx8Z3Jhc3NsYW5kfG1lYWRvdyIpIH4gIlIiLA0KICAgIEjDoWJpdGF0ID0gc3RyX2RldGVjdChIw6FiaXRhdCwgImZvcmVzdCIpIH4gIlQiLA0KICAgIEjDoWJpdGF0ID0gc3RyX2RldGVjdChIw6FiaXRhdCwgIlNjcnVifHNjcnVifFNocnVibGFuZHxzaHJ1YmxhbmR8c2hydWJ8SGVhdGhsYW5kIikgfiAiUyIsDQogICAgSMOhYml0YXQgPSBzdHJfZGV0ZWN0KEjDoWJpdGF0LCAiU3VlbG98U2NyZWV8c2NyZWV8Y2xpZmYiKSB+ICJVIiwNCiAgICBIw6FiaXRhdCA9IGlzLm5hKEjDoWJpdGF0KSB+ICJSIiwNCiAgICBUUlVFIH4gTkFfY2hhcmFjdGVyXyksDQogICAgRVVOSVNhXzFfZGVzY3IgPSBjYXNlX3doZW4oDQogICAgICBFVU5JU2FfMSA9PSAiUiIgfiAiR3Jhc3NsYW5kcyIsDQogICAgICBFVU5JU2FfMSA9PSAiVCIgfiAiRm9yZXN0cyBhbmQgb3RoZXIgd29vZGVkIGxhbmQiLA0KICAgICAgRVVOSVNhXzEgPT0gIlMiIH4gIkhlYXRobGFuZHMsIHNjcnViIGFuZCB0dW5kcmEiLA0KICAgICAgRVVOSVNhXzEgPT0gIlUiIH4gIklubGFuZCBoYWJpdGF0cyB3aXRoIG5vIG9yIGxpdHRsZSBzb2lsIikNCiAgICApDQpgYGANCg0KIyMgTWF4LiBhbmQgbWluLiBORFZJLCBORE1JDQoNCmBgYHtyfQ0KcGxvdF9kaXN0cl9ORFZJX21heF9jb3JkaSA8LSBkaXN0cl9wbG90KGNvcmRpbGxlcmEsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTkRWSV9tYXgiLCAiTkRWSSBtYXgiKQ0KcGxvdF9kaXN0cl9ORFZJX21pbl9jb3JkaSA8LSBkaXN0cl9wbG90KGNvcmRpbGxlcmEsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTkRWSV9taW4iLCAiTkRWSSBtaW4iKQ0KcGxvdF9kaXN0cl9ORE1JX21heF9jb3JkaSA8LSBkaXN0cl9wbG90KGNvcmRpbGxlcmEsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTkRNSV9tYXgiLCAiTkRNSSBtYXgiKQ0KcGxvdF9kaXN0cl9ORE1JX21pbl9jb3JkaSA8LSBkaXN0cl9wbG90KGNvcmRpbGxlcmEsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTkRNSV9taW4iLCAiTkRNSSBtaW4iKQ0KcGxvdF9kaXN0cl9ORFZJX21heF9jb3JkaQ0KcGxvdF9kaXN0cl9ORFZJX21pbl9jb3JkaQ0KcGxvdF9kaXN0cl9ORE1JX21heF9jb3JkaQ0KcGxvdF9kaXN0cl9ORE1JX21pbl9jb3JkaQ0KYGBgDQoNCiMgT0xEIGZyb20gaGVyZQ0KDQojIyMgUGxvdHMgTkRWSSBhbmQgTkRNSQ0KDQpgYGB7cn0NCmdncGxvdChkYXRhID0gY29yZGkgJT4lIGZpbHRlcighaXMubmEoTkRWSV9tYXgpKSAlPiUNCiAgICAgICAgICMgS2VlcCBvbmx5IGZvcmVzdHMsIGdyYXNzbGFuZHMsIHNocnVibGFuZHMgYW5kIHdldGxhbmRzDQogICAgICAgICBmaWx0ZXIoRVVOSVNfMSAlaW4lIGMoIlQiLCAiUiIsICJTIiwgIlEiKSksDQogICAgICAgYWVzKHggPSBFVU5JU18xLCB5ID0gTkRWSV9tYXgsIGZpbGwgPSBFVU5JU18xKSkgKw0KICBnZW9tX2ZsYXRfdmlvbGluKHBvc2l0aW9uID0gcG9zaXRpb25fbnVkZ2UoeCA9IDAuMiwgeSA9IDApLCBhbHBoYSA9IDAuOCkgKw0KICBnZW9tX3BvaW50KGFlcyh5ID0gTkRWSV9tYXgsIGNvbG9yID0gRVVOSVNfMSksDQogICAgICAgICAgICAgcG9zaXRpb24gPSBwb3NpdGlvbl9qaXR0ZXIod2lkdGggPSAwLjE1KSwgc2l6ZSA9IDMsDQogICAgICAgICAgICAgYWxwaGEgPSAwLjUpICsNCiAgZ2VvbV9ib3hwbG90KHdpZHRoID0gMC4yLCBvdXRsaWVyLnNoYXBlID0gTkEsIGFscGhhID0gMC41KSArDQogIHN0YXRfc3VtbWFyeShmdW4ueT1tZWFuLCBnZW9tPSJwb2ludCIsIHNoYXBlID0gMjAsIHNpemUgPSAzKSArDQogIHN0YXRfc3VtbWFyeShmdW4uZGF0YSA9IGZ1bmN0aW9uKHgpIGRhdGEuZnJhbWUoeSA9IG1heCh4KSArIDAuMSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbCA9IGxlbmd0aCh4KSksDQogICAgICAgICAgICAgICBnZW9tID0gInRleHQiLCBhZXMobGFiZWwgPSAuLmxhYmVsLi4pLCB2anVzdCA9IDAuNSkgKw0KICBndWlkZXMoZmlsbCA9IEZBTFNFLCBjb2xvciA9IEZBTFNFKSArIHRoZW1lX2J3KCkgKyBjb29yZF9mbGlwKCkNCmdnc2F2ZShoZXJlKCJvdXRwdXQiLCAiZmlndXJlcyIsICJjb3JkaV9ORFZJX21heC50aWZmIiksDQogICAgICAgd2lkdGggPSAyMSwgaGVpZ2h0ID0gMjkuNywgdW5pdHMgPSAiY20iLCBkcGkgPSAzMDApDQpnZ3Bsb3QoZGF0YSA9IGNvcmRpICU+JSBmaWx0ZXIoIWlzLm5hKE5ETUlfbWF4KSkgJT4lDQogICAgICAgICAjIEtlZXAgb25seSBmb3Jlc3RzLCBncmFzc2xhbmRzLCBzaHJ1YmxhbmRzIGFuZCB3ZXRsYW5kcw0KICAgICAgICAgZmlsdGVyKEVVTklTXzEgJWluJSBjKCJUIiwgIlIiLCAiUyIsICJRIikpLA0KICAgICAgIGFlcyh4ID0gRVVOSVNfMSwgeSA9IE5ETUlfbWF4LCBmaWxsID0gRVVOSVNfMSkpICsNCiAgZ2VvbV9mbGF0X3Zpb2xpbihwb3NpdGlvbiA9IHBvc2l0aW9uX251ZGdlKHggPSAwLjIsIHkgPSAwKSwgYWxwaGEgPSAwLjgpICsNCiAgZ2VvbV9wb2ludChhZXMoeSA9IE5ETUlfbWF4LCBjb2xvciA9IEVVTklTXzEpLA0KICAgICAgICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25faml0dGVyKHdpZHRoID0gMC4xNSksIHNpemUgPSAzLA0KICAgICAgICAgICAgIGFscGhhID0gMC41KSArDQogIGdlb21fYm94cGxvdCh3aWR0aCA9IDAuMiwgb3V0bGllci5zaGFwZSA9IE5BLCBhbHBoYSA9IDAuNSkgKw0KICBzdGF0X3N1bW1hcnkoZnVuLnk9bWVhbiwgZ2VvbT0icG9pbnQiLCBzaGFwZSA9IDIwLCBzaXplID0gMykgKw0KICBzdGF0X3N1bW1hcnkoZnVuLmRhdGEgPSBmdW5jdGlvbih4KSBkYXRhLmZyYW1lKHkgPSBtYXgoeCkgKyAwLjEsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWwgPSBsZW5ndGgoeCkpLA0KICAgICAgICAgICAgICAgZ2VvbSA9ICJ0ZXh0IiwgYWVzKGxhYmVsID0gLi5sYWJlbC4uKSwgdmp1c3QgPSAwLjUpICsNCiAgZ3VpZGVzKGZpbGwgPSBGQUxTRSwgY29sb3IgPSBGQUxTRSkgKyB0aGVtZV9idygpICsgY29vcmRfZmxpcCgpDQpnZ3NhdmUoaGVyZSgib3V0cHV0IiwgImZpZ3VyZXMiLCAiY29yZGlfTkRNSV9tYXgudGlmZiIpLA0KICAgICAgIHdpZHRoID0gMjEsIGhlaWdodCA9IDI5LjcsIHVuaXRzID0gImNtIiwgZHBpID0gMzAwKQ0KYGBgDQoNCiMjIyBNYXBzDQoNCmBgYHtyfQ0KIyBMb2FkIHdvcmxkIGJvdW5kYXJpZXMNCndvcmxkIDwtIG5lX2NvdW50cmllcyhzY2FsZSA9ICJtZWRpdW0iLCByZXR1cm5jbGFzcyA9ICJzZiIpDQpgYGANCg0KYGBge3J9DQojIENhbGN1bGF0ZSB0aGUgZXh0ZW50IG9mIHRoZSBwb2ludHMNCnBvaW50c19jb3JkaV9leHRlbnQgPC0gY29yZGkgJT4lIGZpbHRlcihFVU5JU18xICVpbiUgYygiVCIsICJSIiwgIlMiLCAiUSIpKSAlPiUNCiAgc3VtbWFyaXNlKGxvbl9taW4gPSBtaW4obG9uZ2l0dWRlLCBuYS5ybSA9IFRSVUUpLA0KICAgICAgICAgICAgbG9uX21heCA9IG1heChsb25naXR1ZGUsIG5hLnJtID0gVFJVRSksDQogICAgICAgICAgICBsYXRfbWluID0gbWluKGxhdGl0dWRlLCBuYS5ybSA9IFRSVUUpLA0KICAgICAgICAgICAgbGF0X21heCA9IG1heChsYXRpdHVkZSwgbmEucm0gPSBUUlVFKSkNCg0KIyBBZGQgcGFkZGluZyB0byB0aGUgZXh0ZW50IChhZGp1c3QgYXMgbmVlZGVkKQ0KcGFkZGluZyA8LSAzICAjIEFkanVzdCBwYWRkaW5nIHRvIHlvdXIgcHJlZmVyZW5jZQ0KeF9saW1pdHMgPC0gYyhwb2ludHNfY29yZGlfZXh0ZW50JGxvbl9taW4gLSBwYWRkaW5nLA0KICAgICAgICAgICAgICBwb2ludHNfY29yZGlfZXh0ZW50JGxvbl9tYXggKyBwYWRkaW5nKQ0KeV9saW1pdHMgPC0gYyhwb2ludHNfY29yZGlfZXh0ZW50JGxhdF9taW4gLSBwYWRkaW5nLA0KICAgICAgICAgICAgICBwb2ludHNfY29yZGlfZXh0ZW50JGxhdF9tYXggKyBwYWRkaW5nKQ0KDQojIENyZWF0ZSB0aGUgem9vbWVkIG1hcA0KZ2dwbG90KCkgKw0KICBnZW9tX3NmKGRhdGEgPSB3b3JsZCwgZmlsbCA9ICJsaWdodGJsdWUiLCBjb2xvciA9ICJncmF5IikgKw0KICBnZW9tX3BvaW50KGRhdGEgPSBjb3JkaSAlPiUgZmlsdGVyKEVVTklTXzEgJWluJSBjKCJUIiwgIlIiLCAiUyIsICJRIikpLA0KICAgICAgICAgICAgIGFlcyh4ID0gbG9uZ2l0dWRlLCB5ID0gbGF0aXR1ZGUsIGNvbG9yID0gRVVOSVNfMSksDQogICAgICAgICAgICAgc2l6ZSA9IDEpICsNCiAgY29vcmRfc2YoeGxpbSA9IHhfbGltaXRzLCB5bGltID0geV9saW1pdHMpICsNCiAgdGhlbWVfbWluaW1hbCgpDQpgYGANCg0KIyMgUG9pbnRzIGRpZmZlcmVudGlhbCBHUFMNCg0KIyMjIE1hcHMNCg0KYGBge3J9DQojIENhbGN1bGF0ZSB0aGUgZXh0ZW50IG9mIHRoZSBwb2ludHMNCnBvaW50c19HUFNfZXh0ZW50IDwtIGRiX3Jlc3Vydl9SU19zaG9ydF9QTE9UICU+JQ0KICBmaWx0ZXIoRVVOSVNhXzEgJWluJSBjKCJUIiwgIlIiLCAiUyIsICJRIikpICU+JQ0KICBmaWx0ZXIoUzJfZGF0YSA9PSBUIHwgTGFuZHNhdF9kYXRhID09IFQgKSAlPiUNCiAgZmlsdGVyKGBMb2NhdGlvbiBtZXRob2RgID09ICJMb2NhdGlvbiB3aXRoIGRpZmZlcmVudGlhbCBHUFMiKSAlPiUNCiAgc3VtbWFyaXNlKGxvbl9taW4gPSBtaW4oTG9uX3VwZGF0ZWQsIG5hLnJtID0gVFJVRSksDQogICAgICAgICAgICBsb25fbWF4ID0gbWF4KExvbl91cGRhdGVkLCBuYS5ybSA9IFRSVUUpLA0KICAgICAgICAgICAgbGF0X21pbiA9IG1pbihMYXRfdXBkYXRlZCwgbmEucm0gPSBUUlVFKSwNCiAgICAgICAgICAgIGxhdF9tYXggPSBtYXgoTGF0X3VwZGF0ZWQsIG5hLnJtID0gVFJVRSkpDQoNCiMgQWRkIHBhZGRpbmcgdG8gdGhlIGV4dGVudCAoYWRqdXN0IGFzIG5lZWRlZCkNCnBhZGRpbmcgPC0gMiAgIyBBZGp1c3QgcGFkZGluZyB0byB5b3VyIHByZWZlcmVuY2UNCnhfbGltaXRzIDwtIGMocG9pbnRzX0dQU19leHRlbnQkbG9uX21pbiAtIHBhZGRpbmcsDQogICAgICAgICAgICAgIHBvaW50c19HUFNfZXh0ZW50JGxvbl9tYXggKyBwYWRkaW5nKQ0KeV9saW1pdHMgPC0gYyhwb2ludHNfR1BTX2V4dGVudCRsYXRfbWluIC0gcGFkZGluZywNCiAgICAgICAgICAgICAgcG9pbnRzX0dQU19leHRlbnQkbGF0X21heCArIHBhZGRpbmcpDQoNCiMgQ3JlYXRlIHRoZSB6b29tZWQgbWFwDQpnZ3Bsb3QoKSArDQogIGdlb21fc2YoZGF0YSA9IHdvcmxkLCBmaWxsID0gImxpZ2h0Ymx1ZSIsIGNvbG9yID0gImdyYXkiKSArDQogIGdlb21fcG9pbnQoZGF0YSA9IGRiX3Jlc3Vydl9SU19zaG9ydF9QTE9UICU+JQ0KICAgICAgICAgICAgICAgZmlsdGVyKEVVTklTYV8xICVpbiUgYygiVCIsICJSIiwgIlMiLCAiUSIpKSAlPiUNCiAgICAgICAgICAgICAgIGZpbHRlcihTMl9kYXRhID09IFQgfCBMYW5kc2F0X2RhdGEgPT0gVCApICU+JQ0KICAgICAgICAgICAgICAgZmlsdGVyKGBMb2NhdGlvbiBtZXRob2RgID09ICJMb2NhdGlvbiB3aXRoIGRpZmZlcmVudGlhbCBHUFMiKSwNCiAgICAgICAgICAgICBhZXMoeCA9IExvbl91cGRhdGVkLCB5ID0gTGF0X3VwZGF0ZWQsIGNvbG9yID0gRVVOSVNhXzEpLA0KICAgICAgICAgICAgIHNpemUgPSAxKSArDQogIGNvb3JkX3NmKHhsaW0gPSB4X2xpbWl0cywgeWxpbSA9IHlfbGltaXRzKSArDQogIHRoZW1lX21pbmltYWwoKQ0KYGBgDQoNCk51bWJlciBvZiBkaWZmZXJlbnRpYWwgR1BTIHBvaW50cyBieSBDb3VudHJ5Og0KDQpgYGB7cn0NCmRiX3Jlc3Vydl9SU19zaG9ydF9QTE9UICU+JQ0KICBmaWx0ZXIoRVVOSVNhXzEgJWluJSBjKCJUIiwgIlIiLCAiUyIsICJRIikpICU+JQ0KICBmaWx0ZXIoUzJfZGF0YSA9PSBUIHwgTGFuZHNhdF9kYXRhID09IFQgKSAlPiUNCiAgZmlsdGVyKGBMb2NhdGlvbiBtZXRob2RgID09ICJMb2NhdGlvbiB3aXRoIGRpZmZlcmVudGlhbCBHUFMiKSAlPiUNCiAgY291bnQoQ291bnRyeSkNCmBgYA0KDQojIyBQb2ludHMgUmVTdXJ2ZXkNCg0KIyMjIE1hcHMNCg0KYGBge3J9DQojIENhbGN1bGF0ZSB0aGUgZXh0ZW50IG9mIHRoZSBwb2ludHMNCnBvaW50c19yZXN1cnZleV9leHRlbnQgPC0gZGJfcmVzdXJ2X1JTX3Nob3J0X1BMT1QgJT4lDQogIGZpbHRlcihFVU5JU2FfMSAlaW4lIGMoIlQiLCAiUiIsICJTIiwgIlEiKSkgJT4lDQogIGZpbHRlcihTMl9kYXRhID09IFQgfCBMYW5kc2F0X2RhdGEgPT0gVCApICU+JQ0KICBzdW1tYXJpc2UobG9uX21pbiA9IG1pbihMb25fdXBkYXRlZCwgbmEucm0gPSBUUlVFKSwNCiAgICAgICAgICAgIGxvbl9tYXggPSBtYXgoTG9uX3VwZGF0ZWQsIG5hLnJtID0gVFJVRSksDQogICAgICAgICAgICBsYXRfbWluID0gbWluKExhdF91cGRhdGVkLCBuYS5ybSA9IFRSVUUpLA0KICAgICAgICAgICAgbGF0X21heCA9IG1heChMYXRfdXBkYXRlZCwgbmEucm0gPSBUUlVFKSkNCg0KIyBBZGQgcGFkZGluZyB0byB0aGUgZXh0ZW50IChhZGp1c3QgYXMgbmVlZGVkKQ0KcGFkZGluZyA8LSAyICAjIEFkanVzdCBwYWRkaW5nIHRvIHlvdXIgcHJlZmVyZW5jZQ0KeF9saW1pdHMgPC0gYyhwb2ludHNfcmVzdXJ2ZXlfZXh0ZW50JGxvbl9taW4gLSBwYWRkaW5nLA0KICAgICAgICAgICAgICBwb2ludHNfcmVzdXJ2ZXlfZXh0ZW50JGxvbl9tYXggKyBwYWRkaW5nKQ0KeV9saW1pdHMgPC0gYyhwb2ludHNfcmVzdXJ2ZXlfZXh0ZW50JGxhdF9taW4gLSBwYWRkaW5nLA0KICAgICAgICAgICAgICBwb2ludHNfcmVzdXJ2ZXlfZXh0ZW50JGxhdF9tYXggKyBwYWRkaW5nKQ0KDQojIENyZWF0ZSB0aGUgem9vbWVkIG1hcA0KZ2dwbG90KCkgKw0KICBnZW9tX3NmKGRhdGEgPSB3b3JsZCwgZmlsbCA9ICJsaWdodGJsdWUiLCBjb2xvciA9ICJncmF5IikgKw0KICBnZW9tX3BvaW50KGRhdGEgPSBkYl9yZXN1cnZfUlNfc2hvcnRfUExPVCAlPiUNCiAgICAgICAgICAgICAgIGZpbHRlcihFVU5JU2FfMSAlaW4lIGMoIlQiLCAiUiIsICJTIiwgIlEiKSkgJT4lDQogICAgICAgICAgICAgICBmaWx0ZXIoUzJfZGF0YSA9PSBUIHwgTGFuZHNhdF9kYXRhID09IFQgKSwNCiAgICAgICAgICAgICBhZXMoeCA9IExvbl91cGRhdGVkLCB5ID0gTGF0X3VwZGF0ZWQsIGNvbG9yID0gRVVOSVNhXzEpLA0KICAgICAgICAgICAgIHNpemUgPSAxKSArDQogIGNvb3JkX3NmKHhsaW0gPSB4X2xpbWl0cywgeWxpbSA9IHlfbGltaXRzKSArDQogIHRoZW1lX21pbmltYWwoKQ0KYGBgDQoNCk51bWJlciBvZiBSZVN1cnZleSBwb2ludHMgYnkgQ291bnRyeToNCg0KYGBge3J9DQpkYl9yZXN1cnZfUlNfc2hvcnRfUExPVCAlPiUNCiAgZmlsdGVyKEVVTklTYV8xICVpbiUgYygiVCIsICJSIiwgIlMiLCAiUSIpKSAlPiUNCiAgZmlsdGVyKFMyX2RhdGEgPT0gVCB8IExhbmRzYXRfZGF0YSA9PSBUICkgJT4lDQogIGNvdW50KENvdW50cnkpDQpgYGANCg0KIyMgSm9pbg0KDQpgYGB7cn0NCnBvaW50c19oYWxsZSA8LSBiaW5kX3Jvd3MoDQogIGNvcmRpICU+JSBmaWx0ZXIoRVVOSVNfMSAlaW4lIGMoIlQiLCAiUiIsICJTIiwgIlEiKSkgJT4lDQogICAgc2VsZWN0KGxvbmdpdHVkZSwgbGF0aXR1ZGUsIE5EVklfbWF4LCBORE1JX21heCwgRVVOSVNfMSkgJT4lDQogICAgcmVuYW1lKEVVTklTID0gRVVOSVNfMSkgJT4lDQogICAgbXV0YXRlKHBvaW50X3R5cGUgPSAiQ29yZGlsbGVyYSIpLA0KICBkYl9yZXN1cnZfUlNfc2hvcnRfUExPVCAlPiUNCiAgICBmaWx0ZXIoRVVOSVNhXzEgJWluJSBjKCJUIiwgIlIiLCAiUyIsICJRIikpICU+JQ0KICAgIGZpbHRlcihTMl9kYXRhID09IFQgfCBMYW5kc2F0X2RhdGEgPT0gVCApICU+JQ0KICAgIGZpbHRlcihgTG9jYXRpb24gbWV0aG9kYCA9PSAiTG9jYXRpb24gd2l0aCBkaWZmZXJlbnRpYWwgR1BTIikgJT4lDQogICAgc2VsZWN0KExvbl91cGRhdGVkLCBMYXRfdXBkYXRlZCwgTkRWSV9tYXgsIE5ETUlfbWF4LCBFVU5JU2FfMSkgJT4lDQogICAgcmVuYW1lKGxvbmdpdHVkZSA9IExvbl91cGRhdGVkLCBsYXRpdHVkZSA9IExhdF91cGRhdGVkLA0KICAgICAgICAgICBFVU5JUyA9IEVVTklTYV8xKSAlPiUNCiAgICBtdXRhdGUocG9pbnRfdHlwZSA9ICJSZVN1cnZleSBkaWZmZXJlbnRpYWwgR1BTIiksDQogIGRiX3Jlc3Vydl9SU19zaG9ydF9QTE9UICU+JQ0KICAgIGZpbHRlcihFVU5JU2FfMSAlaW4lIGMoIlQiLCAiUiIsICJTIiwgIlEiKSkgJT4lDQogICAgZmlsdGVyKFMyX2RhdGEgPT0gVCB8IExhbmRzYXRfZGF0YSA9PSBUICkgJT4lDQogICAgc2VsZWN0KExvbl91cGRhdGVkLCBMYXRfdXBkYXRlZCwgTkRWSV9tYXgsIE5ETUlfbWF4LCBFVU5JU2FfMSkgJT4lDQogICAgcmVuYW1lKGxvbmdpdHVkZSA9IExvbl91cGRhdGVkLCBsYXRpdHVkZSA9IExhdF91cGRhdGVkLA0KICAgICAgICAgICBFVU5JUyA9IEVVTklTYV8xKSAlPiUNCiAgICBtdXRhdGUocG9pbnRfdHlwZSA9ICJSZVN1cnZleSIpDQopDQpgYGANCg0KYGBge3J9DQpnZ3Bsb3QoZGF0YSA9IHBvaW50c19oYWxsZSwNCiAgICAgICBhZXMoeCA9IHBvaW50X3R5cGUsIHkgPSBORFZJX21heCwgZmlsbCA9IHBvaW50X3R5cGUpKSArDQogIGdlb21fZmxhdF92aW9saW4ocG9zaXRpb24gPSBwb3NpdGlvbl9udWRnZSh4ID0gMC4yLCB5ID0gMCksIGFscGhhID0gMC44KSArDQogIGdlb21fcG9pbnQoYWVzKHkgPSBORFZJX21heCwgY29sb3IgPSBwb2ludF90eXBlKSwNCiAgICAgICAgICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX2ppdHRlcih3aWR0aCA9IDAuMTUpLCBzaXplID0gMywNCiAgICAgICAgICAgICBhbHBoYSA9IDAuNSkgKw0KICBnZW9tX2JveHBsb3Qod2lkdGggPSAwLjIsIG91dGxpZXIuc2hhcGUgPSBOQSwgYWxwaGEgPSAwLjUpICsNCiAgc3RhdF9zdW1tYXJ5KGZ1bi55PW1lYW4sIGdlb209InBvaW50Iiwgc2hhcGUgPSAyMCwgc2l6ZSA9IDMpICsNCiAgc3RhdF9zdW1tYXJ5KGZ1bi5kYXRhID0gZnVuY3Rpb24oeCkgZGF0YS5mcmFtZSh5ID0gbWF4KHgpICsgMC4xLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVsID0gbGVuZ3RoKHgpKSwNCiAgICAgICAgICAgICAgIGdlb20gPSAidGV4dCIsIGFlcyhsYWJlbCA9IC4ubGFiZWwuLiksIHZqdXN0ID0gMC41KSArDQogIGd1aWRlcyhmaWxsID0gRkFMU0UsIGNvbG9yID0gRkFMU0UpICsgdGhlbWVfYncoKSArIGNvb3JkX2ZsaXAoKSArDQogIGZhY2V0X3dyYXAofiBFVU5JUykNCmBgYA0KDQojIFNlc3Npb24gaW5mbw0KDQpgYGB7cn0NCnNlc3Npb25JbmZvKCkNCmBgYA0KDQo=